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,1262 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ # sage.doctest: needs sage.graphs
3
+ r"""
4
+ Base class for polyhedra: Graph-theoretic methods
5
+
6
+ Define methods relying on :mod:`sage.graphs`.
7
+ """
8
+
9
+ # ****************************************************************************
10
+ # Copyright (C) 2008-2012 Marshall Hampton <hamptonio@gmail.com>
11
+ # Copyright (C) 2011-2015 Volker Braun <vbraun.name@gmail.com>
12
+ # Copyright (C) 2012-2018 Frederic Chapoton
13
+ # Copyright (C) 2013 Andrey Novoseltsev
14
+ # Copyright (C) 2014-2017 Moritz Firsching
15
+ # Copyright (C) 2014-2019 Thierry Monteil
16
+ # Copyright (C) 2015 Nathann Cohen
17
+ # Copyright (C) 2015-2017 Jeroen Demeyer
18
+ # Copyright (C) 2015-2017 Vincent Delecroix
19
+ # Copyright (C) 2015-2018 Dima Pasechnik
20
+ # Copyright (C) 2015-2020 Jean-Philippe Labbe <labbe at math.huji.ac.il>
21
+ # Copyright (C) 2015-2021 Matthias Koeppe
22
+ # Copyright (C) 2016-2019 Daniel Krenn
23
+ # Copyright (C) 2017 Marcelo Forets
24
+ # Copyright (C) 2017-2018 Mark Bell
25
+ # Copyright (C) 2019 Julian Ritter
26
+ # Copyright (C) 2019-2020 Laith Rastanawi
27
+ # Copyright (C) 2019-2020 Sophia Elia
28
+ # Copyright (C) 2019-2021 Jonathan Kliem <jonathan.kliem@gmail.com>
29
+ #
30
+ # This program is free software: you can redistribute it and/or modify
31
+ # it under the terms of the GNU General Public License as published by
32
+ # the Free Software Foundation, either version 2 of the License, or
33
+ # (at your option) any later version.
34
+ # https://www.gnu.org/licenses/
35
+ # ****************************************************************************
36
+
37
+ from sage.misc.cachefunc import cached_method
38
+ from .base3 import Polyhedron_base3
39
+
40
+
41
+ class Polyhedron_base4(Polyhedron_base3):
42
+ """
43
+ Methods relying on :mod:`sage.graphs`.
44
+
45
+ See :class:`sage.geometry.polyhedron.base.Polyhedron_base`.
46
+
47
+ TESTS::
48
+
49
+ sage: from sage.geometry.polyhedron.base4 import Polyhedron_base4
50
+ sage: P = polytopes.cube()
51
+ sage: Polyhedron_base4.vertex_facet_graph.f(P)
52
+ Digraph on 14 vertices
53
+ sage: Polyhedron_base4.vertex_graph(P)
54
+ Graph on 8 vertices
55
+ sage: Polyhedron_base4.face_lattice(P)
56
+ Finite lattice containing 28 elements
57
+ sage: Polyhedron_base4.flag_f_vector(P, 0, 2)
58
+ 24
59
+ sage: Polyhedron_base4.is_self_dual(P)
60
+ False
61
+ sage: Q = polytopes.cube(intervals='zero_one')
62
+ sage: P == Q
63
+ False
64
+ sage: Polyhedron_base4.is_combinatorially_isomorphic(P, Q)
65
+ True
66
+ """
67
+
68
+ @cached_method
69
+ def vertex_facet_graph(self, labels=True):
70
+ r"""
71
+ Return the vertex-facet graph.
72
+
73
+ This function constructs a directed bipartite graph.
74
+ The nodes of the graph correspond to the vertices of the polyhedron
75
+ and the facets of the polyhedron. There is a directed edge
76
+ from a vertex to a face if and only if the vertex is incident to the face.
77
+
78
+ INPUT:
79
+
80
+ - ``labels`` -- boolean (default: ``True``); decide how the nodes
81
+ of the graph are labelled. Either with the original vertices/facets
82
+ of the Polyhedron or with integers.
83
+
84
+ OUTPUT:
85
+
86
+ - a bipartite DiGraph. If ``labels`` is ``True``, then the nodes
87
+ of the graph will actually be the vertices and facets of ``self``,
88
+ otherwise they will be integers.
89
+
90
+ .. SEEALSO::
91
+
92
+ :meth:`combinatorial_automorphism_group`,
93
+ :meth:`is_combinatorially_isomorphic`.
94
+
95
+ EXAMPLES::
96
+
97
+ sage: P = polytopes.cube()
98
+ sage: G = P.vertex_facet_graph(); G
99
+ Digraph on 14 vertices
100
+ sage: G.vertices(sort=True, key=lambda v: str(v))
101
+ [A vertex at (-1, -1, -1),
102
+ A vertex at (-1, -1, 1),
103
+ A vertex at (-1, 1, -1),
104
+ A vertex at (-1, 1, 1),
105
+ A vertex at (1, -1, -1),
106
+ A vertex at (1, -1, 1),
107
+ A vertex at (1, 1, -1),
108
+ A vertex at (1, 1, 1),
109
+ An inequality (-1, 0, 0) x + 1 >= 0,
110
+ An inequality (0, -1, 0) x + 1 >= 0,
111
+ An inequality (0, 0, -1) x + 1 >= 0,
112
+ An inequality (0, 0, 1) x + 1 >= 0,
113
+ An inequality (0, 1, 0) x + 1 >= 0,
114
+ An inequality (1, 0, 0) x + 1 >= 0]
115
+ sage: G.automorphism_group().is_isomorphic(P.hasse_diagram().automorphism_group()) # needs sage.groups
116
+ True
117
+ sage: O = polytopes.octahedron(); O
118
+ A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices
119
+ sage: O.vertex_facet_graph()
120
+ Digraph on 14 vertices
121
+ sage: H = O.vertex_facet_graph()
122
+ sage: G.is_isomorphic(H) # needs sage.groups
123
+ False
124
+ sage: G2 = copy(G)
125
+ sage: G2.reverse_edges(G2.edges(sort=True))
126
+ sage: G2.is_isomorphic(H) # needs sage.groups
127
+ True
128
+
129
+ TESTS:
130
+
131
+ Check that :issue:`28828` is fixed::
132
+
133
+ sage: G._immutable
134
+ True
135
+
136
+ Check that :issue:`29188` is fixed::
137
+
138
+ sage: P = polytopes.cube()
139
+ sage: P.vertex_facet_graph().is_isomorphic(P.vertex_facet_graph(False))
140
+ True
141
+ """
142
+ return self.combinatorial_polyhedron().vertex_facet_graph(names=labels)
143
+
144
+ def vertex_graph(self, **kwds):
145
+ """
146
+ Return a graph in which the vertices correspond to vertices
147
+ of the polyhedron, and edges to edges.
148
+
149
+ INPUT:
150
+
151
+ - ``names`` -- boolean (default: ``True``); if ``False``,
152
+ then the nodes of the graph are labeld by the
153
+ indices of the Vrepresentation
154
+
155
+ - ``algorithm`` -- string (optional);
156
+ specify whether the face generator starts with facets or vertices:
157
+
158
+ * ``'primal'`` -- start with the facets
159
+ * ``'dual'`` -- start with the vertices
160
+ * ``None`` -- choose automatically
161
+
162
+ .. NOTE::
163
+
164
+ The graph of a polyhedron with lines has no vertices,
165
+ as the polyhedron has no vertices (`0`-faces).
166
+
167
+ The method :meth:`~sage.geometry.polyhedron.base0.Polyhedron_base0.vertices` returns
168
+ the defining points in this case.
169
+
170
+ EXAMPLES::
171
+
172
+ sage: g3 = polytopes.hypercube(3).vertex_graph(); g3
173
+ Graph on 8 vertices
174
+ sage: g3.automorphism_group().cardinality() # needs sage.groups
175
+ 48
176
+ sage: s4 = polytopes.simplex(4).vertex_graph(); s4
177
+ Graph on 5 vertices
178
+ sage: s4.is_eulerian()
179
+ True
180
+
181
+ The graph of an unbounded polyhedron
182
+ is the graph of the bounded complex::
183
+
184
+ sage: open_triangle = Polyhedron(vertices=[[1,0], [0,1]],
185
+ ....: rays =[[1,1]])
186
+ sage: open_triangle.vertex_graph()
187
+ Graph on 2 vertices
188
+
189
+ The graph of a polyhedron with lines has no vertices::
190
+
191
+ sage: line = Polyhedron(lines=[[0,1]])
192
+ sage: line.vertex_graph()
193
+ Graph on 0 vertices
194
+
195
+ TESTS:
196
+
197
+ Check for a line segment (:issue:`30545`)::
198
+
199
+ sage: polytopes.simplex(1).graph().edges(sort=True)
200
+ [(A vertex at (0, 1), A vertex at (1, 0), None)]
201
+ """
202
+ return self.combinatorial_polyhedron().vertex_graph(**kwds)
203
+
204
+ graph = vertex_graph
205
+
206
+ def vertex_digraph(self, f, increasing=True):
207
+ r"""
208
+ Return the directed graph of the polyhedron according to a linear form.
209
+
210
+ The underlying undirected graph is the graph of vertices and edges.
211
+
212
+ INPUT:
213
+
214
+ - ``f`` -- a linear form. The linear form can be provided as:
215
+
216
+ - a vector space morphism with one-dimensional codomain, (see
217
+ :func:`sage.modules.vector_space_morphism.linear_transformation`
218
+ and
219
+ :class:`sage.modules.vector_space_morphism.VectorSpaceMorphism`)
220
+ - a vector ; in this case the linear form is obtained by duality
221
+ using the dot product: ``f(v) = v.dot_product(f)``.
222
+
223
+ - ``increasing`` -- boolean (default: ``True``); whether to orient
224
+ edges in the increasing or decreasing direction
225
+
226
+ By default, an edge is oriented from `v` to `w` if
227
+ `f(v) \leq f(w)`.
228
+
229
+ If `f(v)=f(w)`, then two opposite edges are created.
230
+
231
+ EXAMPLES::
232
+
233
+ sage: penta = Polyhedron([[0,0],[1,0],[0,1],[1,2],[3,2]])
234
+ sage: G = penta.vertex_digraph(vector([1,1])); G
235
+ Digraph on 5 vertices
236
+ sage: G.sinks()
237
+ [A vertex at (3, 2)]
238
+
239
+ sage: A = matrix(ZZ, [[1], [-1]])
240
+ sage: f = linear_transformation(A)
241
+ sage: G = penta.vertex_digraph(f) ; G
242
+ Digraph on 5 vertices
243
+ sage: G.is_directed_acyclic()
244
+ False
245
+
246
+ .. SEEALSO::
247
+
248
+ :meth:`vertex_graph`
249
+ """
250
+ from sage.modules.vector_space_morphism import VectorSpaceMorphism
251
+ if isinstance(f, VectorSpaceMorphism):
252
+ if f.codomain().dimension() == 1:
253
+ orientation_check = lambda v: f(v) >= 0
254
+ else:
255
+ raise TypeError('the linear map f must have '
256
+ 'one-dimensional codomain')
257
+ else:
258
+ try:
259
+ if f.is_vector():
260
+ orientation_check = lambda v: v.dot_product(f) >= 0
261
+ else:
262
+ raise TypeError('f must be a linear map or a vector')
263
+ except AttributeError:
264
+ raise TypeError('f must be a linear map or a vector')
265
+ if not increasing:
266
+ f = -f
267
+ from sage.graphs.digraph import DiGraph
268
+ dg = DiGraph()
269
+ for j in range(self.n_vertices()):
270
+ vj = self.Vrepresentation(j)
271
+ for vi in vj.neighbors():
272
+ if orientation_check(vj.vector() - vi.vector()):
273
+ dg.add_edge(vi, vj)
274
+ return dg
275
+
276
+ def face_lattice(self):
277
+ """
278
+ Return the face-lattice poset.
279
+
280
+ OUTPUT:
281
+
282
+ A :class:`~sage.combinat.posets.posets.FinitePoset`. Elements
283
+ are given as
284
+ :class:`~sage.geometry.polyhedron.face.PolyhedronFace`.
285
+
286
+ In the case of a full-dimensional polytope, the faces are
287
+ pairs (vertices, inequalities) of the spanning vertices and
288
+ corresponding saturated inequalities. In general, a face is
289
+ defined by a pair (V-rep. objects, H-rep. objects). The
290
+ V-representation objects span the face, and the corresponding
291
+ H-representation objects are those inequalities and equations
292
+ that are saturated on the face.
293
+
294
+ The bottom-most element of the face lattice is the "empty
295
+ face". It contains no V-representation object. All
296
+ H-representation objects are incident.
297
+
298
+ The top-most element is the "full face". It is spanned by all
299
+ V-representation objects. The incident H-representation
300
+ objects are all equations and no inequalities.
301
+
302
+ In the case of a full-dimensional polytope, the "empty face"
303
+ and the "full face" are the empty set (no vertices, all
304
+ inequalities) and the full polytope (all vertices, no
305
+ inequalities), respectively.
306
+
307
+ ALGORITHM:
308
+
309
+ See :mod:`sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator`.
310
+
311
+ .. NOTE::
312
+
313
+ The face lattice is not cached, as long as this creates a memory leak, see :issue:`28982`.
314
+
315
+ EXAMPLES::
316
+
317
+ sage: square = polytopes.hypercube(2)
318
+ sage: fl = square.face_lattice();fl
319
+ Finite lattice containing 10 elements
320
+ sage: list(f.ambient_V_indices() for f in fl)
321
+ [(), (0,), (1,), (0, 1), (2,), (1, 2), (3,), (0, 3), (2, 3), (0, 1, 2, 3)]
322
+ sage: poset_element = fl[5]
323
+ sage: a_face = poset_element
324
+ sage: a_face
325
+ A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 2 vertices
326
+ sage: a_face.ambient_V_indices()
327
+ (1, 2)
328
+ sage: set(a_face.ambient_Vrepresentation()) == \
329
+ ....: set([square.Vrepresentation(1), square.Vrepresentation(2)])
330
+ True
331
+ sage: a_face.ambient_Vrepresentation()
332
+ (A vertex at (1, 1), A vertex at (-1, 1))
333
+ sage: a_face.ambient_Hrepresentation()
334
+ (An inequality (0, -1) x + 1 >= 0,)
335
+
336
+ A more complicated example::
337
+
338
+ sage: c5_10 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5] for i in range(1,11)])
339
+ sage: c5_10_fl = c5_10.face_lattice()
340
+ sage: [len(x) for x in c5_10_fl.level_sets()]
341
+ [1, 10, 45, 100, 105, 42, 1]
342
+
343
+ Note that if the polyhedron contains lines then there is a
344
+ dimension gap between the empty face and the first non-empty
345
+ face in the face lattice::
346
+
347
+ sage: line = Polyhedron(vertices=[(0,)], lines=[(1,)])
348
+ sage: [ fl.dim() for fl in line.face_lattice() ]
349
+ [-1, 1]
350
+
351
+ TESTS::
352
+
353
+ sage: c5_20 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5]
354
+ ....: for i in range(1,21)])
355
+ sage: c5_20_fl = c5_20.face_lattice() # long time
356
+ sage: [len(x) for x in c5_20_fl.level_sets()] # long time
357
+ [1, 20, 190, 580, 680, 272, 1]
358
+ sage: polytopes.hypercube(2).face_lattice().plot() # needs sage.plot
359
+ Graphics object consisting of 27 graphics primitives
360
+ sage: level_sets = polytopes.cross_polytope(2).face_lattice().level_sets()
361
+ sage: level_sets[0][0].ambient_V_indices(), level_sets[-1][0].ambient_V_indices()
362
+ ((), (0, 1, 2, 3))
363
+
364
+ Various degenerate polyhedra::
365
+
366
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[[0,0,0],[1,0,0],[0,1,0]]).face_lattice().level_sets()]
367
+ [[()], [(0,), (1,), (2,)], [(0, 1), (0, 2), (1, 2)], [(0, 1, 2)]]
368
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(1,0,0),(0,1,0)], rays=[(0,0,1)]).face_lattice().level_sets()]
369
+ [[()], [(1,), (2,)], [(0, 1), (0, 2), (1, 2)], [(0, 1, 2)]]
370
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(rays=[(1,0,0),(0,1,0)], vertices=[(0,0,1)]).face_lattice().level_sets()]
371
+ [[()], [(0,)], [(0, 1), (0, 2)], [(0, 1, 2)]]
372
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(rays=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()]
373
+ [[()], [(0,)], [(0, 1), (0, 2)], [(0, 1, 2)]]
374
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(1,),(0,)]).face_lattice().level_sets()]
375
+ [[()], [(0,), (1,)], [(0, 1)]]
376
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(1,0,0),(0,1,0)], lines=[(0,0,1)]).face_lattice().level_sets()]
377
+ [[()], [(0, 1), (0, 2)], [(0, 1, 2)]]
378
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0,0)], vertices=[(0,0,1)]).face_lattice().level_sets()]
379
+ [[()], [(0, 1)]]
380
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()]
381
+ [[()], [(0, 1, 2)]]
382
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0)], rays=[(0,1)], vertices=[(0,0)]).face_lattice().level_sets()]
383
+ [[()], [(0, 1)], [(0, 1, 2)]]
384
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(0,)], lines=[(1,)]).face_lattice().level_sets()]
385
+ [[()], [(0, 1)]]
386
+ sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0)], vertices=[(0,0)]).face_lattice().level_sets()]
387
+ [[()], [(0, 1)]]
388
+ """
389
+ from sage.combinat.posets.lattices import FiniteLatticePoset
390
+ return FiniteLatticePoset(self.hasse_diagram())
391
+
392
+ @cached_method
393
+ def hasse_diagram(self):
394
+ r"""
395
+ Return the Hasse diagram of the face lattice of ``self``.
396
+
397
+ This is the Hasse diagram of the poset of the faces of ``self``.
398
+
399
+ OUTPUT: a directed graph
400
+
401
+ EXAMPLES::
402
+
403
+ sage: # needs sage.rings.number_field
404
+ sage: P = polytopes.regular_polygon(4).pyramid()
405
+ sage: D = P.hasse_diagram(); D
406
+ Digraph on 20 vertices
407
+ sage: D.degree_polynomial()
408
+ x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
409
+
410
+ Faces of a mutable polyhedron are not hashable. Hence those are not suitable as
411
+ vertices of the hasse diagram. Use the combinatorial polyhedron instead::
412
+
413
+ sage: # needs sage.rings.number_field
414
+ sage: P = polytopes.regular_polygon(4).pyramid()
415
+ sage: parent = P.parent()
416
+ sage: parent = parent.change_ring(QQ, backend='ppl')
417
+ sage: Q = parent._element_constructor_(P, mutable=True)
418
+ sage: Q.hasse_diagram()
419
+ Traceback (most recent call last):
420
+ ...
421
+ TypeError: ...mutable polyhedra are unhashable...
422
+ sage: C = Q.combinatorial_polyhedron()
423
+ sage: D = C.hasse_diagram()
424
+ sage: set(D.vertices(sort=False)) == set(range(20))
425
+ True
426
+ sage: def index_to_combinatorial_face(n):
427
+ ....: return C.face_by_face_lattice_index(n)
428
+ sage: D.relabel(index_to_combinatorial_face, inplace=True)
429
+ sage: D.vertices(sort=True)
430
+ [A -1-dimensional face of a 3-dimensional combinatorial polyhedron,
431
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
432
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
433
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
434
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
435
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
436
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
437
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
438
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
439
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
440
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
441
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
442
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
443
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
444
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
445
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
446
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
447
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
448
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
449
+ A 3-dimensional face of a 3-dimensional combinatorial polyhedron]
450
+ sage: D.degree_polynomial()
451
+ x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
452
+ """
453
+
454
+ from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face
455
+ C = self.combinatorial_polyhedron()
456
+ D = C.hasse_diagram()
457
+
458
+ def index_to_polyhedron_face(n):
459
+ return combinatorial_face_to_polyhedral_face(
460
+ self, C.face_by_face_lattice_index(n))
461
+
462
+ return D.relabel(index_to_polyhedron_face, inplace=False, immutable=True)
463
+
464
+ def flag_f_vector(self, *args):
465
+ r"""
466
+ Return the flag f-vector.
467
+
468
+ For each `-1 < i_0 < \dots < i_n < d` the flag f-vector
469
+ counts the number of flags `F_0 \subset \dots \subset F_n`
470
+ with `F_j` of dimension `i_j` for each `0 \leq j \leq n`,
471
+ where `d` is the dimension of the polyhedron.
472
+
473
+ INPUT:
474
+
475
+ - ``args`` -- integer (optional); specify an entry of the
476
+ flag-f-vector; must be an increasing sequence of integers
477
+
478
+ OUTPUT: a dictionary, if no arguments were given
479
+
480
+ - an Integer, if arguments were given
481
+
482
+ EXAMPLES:
483
+
484
+ Obtain the entire flag-f-vector::
485
+
486
+ sage: P = polytopes.twenty_four_cell()
487
+ sage: P.flag_f_vector()
488
+ {(-1,): 1,
489
+ (0,): 24,
490
+ (0, 1): 192,
491
+ (0, 1, 2): 576,
492
+ (0, 1, 2, 3): 1152,
493
+ (0, 1, 3): 576,
494
+ (0, 2): 288,
495
+ (0, 2, 3): 576,
496
+ (0, 3): 144,
497
+ (1,): 96,
498
+ (1, 2): 288,
499
+ (1, 2, 3): 576,
500
+ (1, 3): 288,
501
+ (2,): 96,
502
+ (2, 3): 192,
503
+ (3,): 24,
504
+ (4,): 1}
505
+
506
+ Specify an entry::
507
+
508
+ sage: P.flag_f_vector(0,3)
509
+ 144
510
+ sage: P.flag_f_vector(2)
511
+ 96
512
+
513
+ Leading ``-1`` and trailing entry of dimension are allowed::
514
+
515
+ sage: P.flag_f_vector(-1,0,3)
516
+ 144
517
+ sage: P.flag_f_vector(-1,0,3,4)
518
+ 144
519
+
520
+ One can get the number of trivial faces::
521
+
522
+ sage: P.flag_f_vector(-1)
523
+ 1
524
+ sage: P.flag_f_vector(4)
525
+ 1
526
+
527
+ Polyhedra with lines, have ``0`` entries accordingly::
528
+
529
+ sage: P = (Polyhedron(lines=[[1]]) * polytopes.cross_polytope(3))
530
+ sage: P.flag_f_vector()
531
+ {(-1,): 1,
532
+ (0, 1): 0,
533
+ (0, 1, 2): 0,
534
+ (0, 1, 3): 0,
535
+ (0, 2): 0,
536
+ (0, 2, 3): 0,
537
+ (0, 3): 0,
538
+ (0,): 0,
539
+ (1, 2): 24,
540
+ (1, 2, 3): 48,
541
+ (1, 3): 24,
542
+ (1,): 6,
543
+ (2, 3): 24,
544
+ (2,): 12,
545
+ (3,): 8,
546
+ 4: 1}
547
+
548
+ If the arguments are not strictly increasing or out of range,
549
+ a key error is raised::
550
+
551
+ sage: P.flag_f_vector(-1,0,3,6)
552
+ Traceback (most recent call last):
553
+ ...
554
+ KeyError: (0, 3, 6)
555
+ sage: P.flag_f_vector(-1,3,0)
556
+ Traceback (most recent call last):
557
+ ...
558
+ KeyError: (3, 0)
559
+ """
560
+ flag = self._flag_f_vector()
561
+ if len(args) == 0:
562
+ return flag
563
+ elif len(args) == 1:
564
+ return flag[(args[0],)]
565
+ else:
566
+ dim = self.dimension()
567
+ if args[0] == -1:
568
+ args = args[1:]
569
+ if args[-1] == dim:
570
+ args = args[:-1]
571
+ return flag[tuple(args)]
572
+
573
+ @cached_method(do_pickle=True)
574
+ def _flag_f_vector(self):
575
+ r"""
576
+ Return the flag-f-vector.
577
+
578
+ See :meth:`flag_f_vector`.
579
+
580
+ TESTS::
581
+
582
+ sage: polytopes.hypercube(4)._flag_f_vector()
583
+ {(-1,): 1,
584
+ (0,): 16,
585
+ (0, 1): 64,
586
+ (0, 1, 2): 192,
587
+ (0, 1, 2, 3): 384,
588
+ (0, 1, 3): 192,
589
+ (0, 2): 96,
590
+ (0, 2, 3): 192,
591
+ (0, 3): 64,
592
+ (1,): 32,
593
+ (1, 2): 96,
594
+ (1, 2, 3): 192,
595
+ (1, 3): 96,
596
+ (2,): 24,
597
+ (2, 3): 48,
598
+ (3,): 8,
599
+ (4,): 1}
600
+ """
601
+ return self.combinatorial_polyhedron()._flag_f_vector()
602
+
603
+ @cached_method
604
+ def combinatorial_automorphism_group(self, vertex_graph_only=False):
605
+ """
606
+ Compute the combinatorial automorphism group.
607
+
608
+ If ``vertex_graph_only`` is ``True``, the automorphism group
609
+ of the vertex-edge graph of the polyhedron is returned. Otherwise
610
+ the automorphism group of the vertex-facet graph, which is
611
+ isomorphic to the automorphism group of the face lattice is returned.
612
+
613
+ INPUT:
614
+
615
+ - ``vertex_graph_only`` -- boolean (default: ``False``); whether
616
+ to return the automorphism group of the vertex edges graph or
617
+ of the lattice
618
+
619
+ OUTPUT:
620
+
621
+ A
622
+ :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic_with_category'>`
623
+ that is isomorphic to the combinatorial automorphism group is
624
+ returned.
625
+
626
+ - if ``vertex_graph_only`` is ``True``:
627
+ The automorphism group of the vertex-edge graph of the polyhedron
628
+
629
+ - if ``vertex_graph_only`` is ``False`` (default):
630
+ The automorphism group of the vertex-facet graph of the polyhedron,
631
+ see :meth:`vertex_facet_graph`. This group is isomorphic to the
632
+ automorphism group of the face lattice of the polyhedron.
633
+
634
+ NOTE:
635
+
636
+ Depending on ``vertex_graph_only``, this method returns groups
637
+ that are not necessarily isomorphic, see the examples below.
638
+
639
+ .. SEEALSO::
640
+
641
+ :meth:`is_combinatorially_isomorphic`,
642
+ :meth:`graph`,
643
+ :meth:`vertex_facet_graph`.
644
+
645
+ EXAMPLES::
646
+
647
+ sage: quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)])
648
+ sage: quadrangle.combinatorial_automorphism_group().is_isomorphic( # needs sage.groups
649
+ ....: groups.permutation.Dihedral(4))
650
+ True
651
+ sage: quadrangle.restricted_automorphism_group() # needs sage.groups
652
+ Permutation Group with generators [()]
653
+
654
+ Permutations of the vertex graph only exchange vertices with vertices::
655
+
656
+ sage: P = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)])
657
+ sage: P.combinatorial_automorphism_group(vertex_graph_only=True) # needs sage.groups
658
+ Permutation Group with generators [(A vertex at (1,0),A vertex at (1,1))]
659
+
660
+ This shows an example of two polytopes whose vertex-edge graphs are isomorphic,
661
+ but their face lattices are not isomorphic::
662
+
663
+ sage: # needs sage.groups
664
+ sage: Q = Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773],
665
+ ....: [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817],
666
+ ....: [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199],
667
+ ....: [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097],
668
+ ....: [-19184/902877, 26136/300959, -21472/902877, 899005/902877],
669
+ ....: [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933],
670
+ ....: [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433],
671
+ ....: [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]])
672
+ sage: C = polytopes.cyclic_polytope(4,8)
673
+ sage: C.is_combinatorially_isomorphic(Q)
674
+ False
675
+ sage: C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic(
676
+ ....: Q.combinatorial_automorphism_group(vertex_graph_only=True))
677
+ True
678
+ sage: C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic(
679
+ ....: Q.combinatorial_automorphism_group(vertex_graph_only=False))
680
+ False
681
+
682
+ The automorphism group of the face lattice is isomorphic to the combinatorial automorphism group::
683
+
684
+ sage: # needs sage.groups
685
+ sage: CG = C.hasse_diagram().automorphism_group()
686
+ sage: C.combinatorial_automorphism_group().is_isomorphic(CG)
687
+ True
688
+ sage: QG = Q.hasse_diagram().automorphism_group()
689
+ sage: Q.combinatorial_automorphism_group().is_isomorphic(QG)
690
+ True
691
+ """
692
+ if vertex_graph_only:
693
+ G = self.graph()
694
+ else:
695
+ G = self.vertex_facet_graph()
696
+ return G.automorphism_group(edge_labels=True)
697
+
698
+ @cached_method
699
+ def restricted_automorphism_group(self, output='abstract'):
700
+ r"""
701
+ Return the restricted automorphism group.
702
+
703
+ First, let the linear automorphism group be the subgroup of
704
+ the affine group `AGL(d,\RR) = GL(d,\RR) \ltimes \RR^d`
705
+ preserving the `d`-dimensional polyhedron. The affine group
706
+ acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the
707
+ ambient space.
708
+
709
+ The restricted automorphism group is the subgroup of the linear
710
+ automorphism group generated by permutations of the generators
711
+ of the same type. That is, vertices can only be permuted with
712
+ vertices, ray generators with ray generators, and line
713
+ generators with line generators.
714
+
715
+ For example, take the first quadrant
716
+
717
+ .. MATH::
718
+
719
+ Q = \Big\{ (x,y) \Big| x\geq 0,\; y\geq0 \Big\}
720
+ \subset \QQ^2
721
+
722
+ Then the linear automorphism group is
723
+
724
+ .. MATH::
725
+
726
+ \mathrm{Aut}(Q) =
727
+ \left\{
728
+ \begin{pmatrix}
729
+ a & 0 \\ 0 & b
730
+ \end{pmatrix}
731
+ ,~
732
+ \begin{pmatrix}
733
+ 0 & c \\ d & 0
734
+ \end{pmatrix}
735
+ :~
736
+ a, b, c, d \in \QQ_{>0}
737
+ \right\}
738
+ \subset
739
+ GL(2,\QQ)
740
+ \subset
741
+ E(d)
742
+
743
+ Note that there are no translations that map the quadrant `Q`
744
+ to itself, so the linear automorphism group is contained in
745
+ the general linear group (the subgroup of transformations
746
+ preserving the origin). The restricted automorphism group is
747
+
748
+ .. MATH::
749
+
750
+ \mathrm{Aut}(Q) =
751
+ \left\{
752
+ \begin{pmatrix}
753
+ 1 & 0 \\ 0 & 1
754
+ \end{pmatrix}
755
+ ,~
756
+ \begin{pmatrix}
757
+ 0 & 1 \\ 1 & 0
758
+ \end{pmatrix}
759
+ \right\}
760
+ \simeq \ZZ_2
761
+
762
+ INPUT:
763
+
764
+ - ``output`` -- how the group should be represented:
765
+
766
+ - ``'abstract'`` -- default; return an abstract permutation
767
+ group without further meaning
768
+
769
+ - ``'permutation'`` -- return a permutation group on the
770
+ indices of the polyhedron generators. For example, the
771
+ permutation ``(0,1)`` would correspond to swapping
772
+ ``self.Vrepresentation(0)`` and ``self.Vrepresentation(1)``.
773
+
774
+ - ``'matrix'`` -- return a matrix group representing affine
775
+ transformations. When acting on affine vectors, you should
776
+ append a `1` to every vector. If the polyhedron is not full
777
+ dimensional, the returned matrices act as the identity on
778
+ the orthogonal complement of the affine space spanned by
779
+ the polyhedron.
780
+
781
+ - ``'matrixlist'`` -- like ``matrix``, but return the list of
782
+ elements of the matrix group. Useful for fields without a
783
+ good implementation of matrix groups or to avoid the
784
+ overhead of creating the group.
785
+
786
+ OUTPUT:
787
+
788
+ - For ``output="abstract"`` and ``output="permutation"``:
789
+ a :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`.
790
+
791
+ - For ``output="matrix"``: a :func:`~sage.groups.matrix_gps.finitely_generated.MatrixGroup`.
792
+
793
+ - For ``output="matrixlist"``: a list of matrices.
794
+
795
+ REFERENCES:
796
+
797
+ - [BSS2009]_
798
+
799
+ EXAMPLES:
800
+
801
+ A cross-polytope example::
802
+
803
+ sage: # needs sage.groups
804
+ sage: P = polytopes.cross_polytope(3)
805
+ sage: P.restricted_automorphism_group() == PermutationGroup([[(3,4)], [(2,3),(4,5)],[(2,5)],[(1,2),(5,6)],[(1,6)]])
806
+ True
807
+ sage: P.restricted_automorphism_group(output='permutation') == PermutationGroup([[(2,3)],[(1,2),(3,4)],[(1,4)],[(0,1),(4,5)],[(0,5)]])
808
+ True
809
+ sage: mgens = [[[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]], [[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]], [[0,1,0,0],[1,0,0,0],[0,0,1,0],[0,0,0,1]]]
810
+
811
+ We test groups for equality in a fool-proof way; they can have different generators, etc::
812
+
813
+ sage: # needs sage.groups
814
+ sage: poly_g = P.restricted_automorphism_group(output='matrix')
815
+ sage: matrix_g = MatrixGroup([matrix(QQ,t) for t in mgens])
816
+ sage: all(t.matrix() in poly_g for t in matrix_g.gens())
817
+ True
818
+ sage: all(t.matrix() in matrix_g for t in poly_g.gens())
819
+ True
820
+
821
+ 24-cell example::
822
+
823
+ sage: # needs sage.groups
824
+ sage: P24 = polytopes.twenty_four_cell()
825
+ sage: AutP24 = P24.restricted_automorphism_group()
826
+ sage: PermutationGroup([
827
+ ....: '(1,20,2,24,5,23)(3,18,10,19,4,14)(6,21,11,22,7,15)(8,12,16,17,13,9)',
828
+ ....: '(1,21,8,24,4,17)(2,11,6,15,9,13)(3,20)(5,22)(10,16,12,23,14,19)'
829
+ ....: ]).is_isomorphic(AutP24)
830
+ True
831
+ sage: AutP24.order()
832
+ 1152
833
+
834
+ Here is the quadrant example mentioned in the beginning::
835
+
836
+ sage: # needs sage.groups
837
+ sage: P = Polyhedron(rays=[(1,0),(0,1)])
838
+ sage: P.Vrepresentation()
839
+ (A vertex at (0, 0), A ray in the direction (0, 1), A ray in the direction (1, 0))
840
+ sage: P.restricted_automorphism_group(output='permutation')
841
+ Permutation Group with generators [(1,2)]
842
+
843
+ Also, the polyhedron need not be full-dimensional::
844
+
845
+ sage: # needs sage.groups
846
+ sage: P = Polyhedron(vertices=[(1,2,3,4,5),(7,8,9,10,11)])
847
+ sage: P.restricted_automorphism_group()
848
+ Permutation Group with generators [(1,2)]
849
+ sage: G = P.restricted_automorphism_group(output='matrixlist'); G
850
+ (
851
+ [1 0 0 0 0 0] [ -87/55 -82/55 -2/5 38/55 98/55 12/11]
852
+ [0 1 0 0 0 0] [-142/55 -27/55 -2/5 38/55 98/55 12/11]
853
+ [0 0 1 0 0 0] [-142/55 -82/55 3/5 38/55 98/55 12/11]
854
+ [0 0 0 1 0 0] [-142/55 -82/55 -2/5 93/55 98/55 12/11]
855
+ [0 0 0 0 1 0] [-142/55 -82/55 -2/5 38/55 153/55 12/11]
856
+ [0 0 0 0 0 1], [ 0 0 0 0 0 1]
857
+ )
858
+ sage: g = AffineGroup(5, QQ)(G[1]); g
859
+ [ -87/55 -82/55 -2/5 38/55 98/55] [12/11]
860
+ [-142/55 -27/55 -2/5 38/55 98/55] [12/11]
861
+ x |-> [-142/55 -82/55 3/5 38/55 98/55] x + [12/11]
862
+ [-142/55 -82/55 -2/5 93/55 98/55] [12/11]
863
+ [-142/55 -82/55 -2/5 38/55 153/55] [12/11]
864
+ sage: g^2
865
+ [1 0 0 0 0] [0]
866
+ [0 1 0 0 0] [0]
867
+ x |-> [0 0 1 0 0] x + [0]
868
+ [0 0 0 1 0] [0]
869
+ [0 0 0 0 1] [0]
870
+ sage: g(list(P.vertices()[0]))
871
+ (7, 8, 9, 10, 11)
872
+ sage: g(list(P.vertices()[1]))
873
+ (1, 2, 3, 4, 5)
874
+
875
+ Affine transformations do not change the restricted automorphism
876
+ group. For example, any non-degenerate triangle has the
877
+ dihedral group with 6 elements, `D_6`, as its automorphism
878
+ group::
879
+
880
+ sage: # needs sage.groups
881
+ sage: initial_points = [vector([1,0]), vector([0,1]), vector([-2,-1])]
882
+ sage: points = initial_points
883
+ sage: Polyhedron(vertices=points).restricted_automorphism_group()
884
+ Permutation Group with generators [(2,3), (1,2)]
885
+ sage: points = [pt - initial_points[0] for pt in initial_points]
886
+ sage: Polyhedron(vertices=points).restricted_automorphism_group()
887
+ Permutation Group with generators [(2,3), (1,2)]
888
+ sage: points = [pt - initial_points[1] for pt in initial_points]
889
+ sage: Polyhedron(vertices=points).restricted_automorphism_group()
890
+ Permutation Group with generators [(2,3), (1,2)]
891
+ sage: points = [pt - 2*initial_points[1] for pt in initial_points]
892
+ sage: Polyhedron(vertices=points).restricted_automorphism_group()
893
+ Permutation Group with generators [(2,3), (1,2)]
894
+
895
+ The ``output="matrixlist"`` can be used over fields without a
896
+ complete implementation of matrix groups::
897
+
898
+ sage: # needs sage.groups sage.rings.number_field
899
+ sage: P = polytopes.dodecahedron(); P
900
+ A 3-dimensional polyhedron in (Number Field in sqrt5 with defining
901
+ polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^3
902
+ defined as the convex hull of 20 vertices
903
+ sage: G = P.restricted_automorphism_group(output='matrixlist')
904
+ sage: len(G)
905
+ 120
906
+
907
+ Floating-point computations are supported with a simple fuzzy
908
+ zero implementation::
909
+
910
+ sage: P = Polyhedron(vertices=[(1/3,0,0,1),(0,1/4,0,1),(0,0,1/5,1)],
911
+ ....: base_ring=RDF)
912
+ sage: P.restricted_automorphism_group() # needs sage.groups
913
+ Permutation Group with generators [(2,3), (1,2)]
914
+ sage: len(P.restricted_automorphism_group(output='matrixlist'))
915
+ 6
916
+
917
+ TESTS::
918
+
919
+ sage: P = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)])
920
+ sage: P.restricted_automorphism_group(output='permutation') # needs sage.groups
921
+ Permutation Group with generators [(1,2)]
922
+ sage: P.restricted_automorphism_group(output='matrix')
923
+ Matrix group over Rational Field with 1 generators (
924
+ [ 1 0 0]
925
+ [ 0 -1 1]
926
+ [ 0 0 1]
927
+ )
928
+ sage: P.restricted_automorphism_group(output='foobar')
929
+ Traceback (most recent call last):
930
+ ...
931
+ ValueError: unknown output 'foobar', valid values are
932
+ ('abstract', 'permutation', 'matrix', 'matrixlist')
933
+
934
+ Check that :issue:`28828` is fixed::
935
+
936
+ sage: P.restricted_automorphism_group(output='matrixlist')[0].is_immutable()
937
+ True
938
+ """
939
+ # The algorithm works as follows:
940
+ #
941
+ # Let V be the matrix where every column is a homogeneous
942
+ # coordinate of a V-representation object (vertex, ray, line).
943
+ # Let us assume that V has full rank, that the polyhedron is
944
+ # full dimensional.
945
+ #
946
+ # Let Q = V Vt and C = Vt Q^-1 V. The rows and columns of C
947
+ # can be thought of as being indexed by the V-rep objects of the
948
+ # polytope.
949
+ #
950
+ # It turns out that we can identify the restricted automorphism
951
+ # group with the automorphism group of the edge-colored graph
952
+ # on the V-rep objects with colors determined by the symmetric
953
+ # matrix C.
954
+ #
955
+ # An automorphism of this graph is equivalent to a permutation
956
+ # matrix P such that C = Pt C P. If we now define
957
+ # A = V P Vt Q^-1, then one can check that V P = A V.
958
+ # In other words: permuting the generators is the same as
959
+ # applying the affine transformation A on the generators.
960
+ #
961
+ # If the given polyhedron is not fully-dimensional,
962
+ # then Q will be not invertible. In this case, we use a
963
+ # pseudoinverse Q+ instead of Q^-1. The formula for A acting on
964
+ # the space spanned by V then simplifies to A = V P V+ where V+
965
+ # denotes the pseudoinverse of V, which also equals V+ = Vt Q+.
966
+ #
967
+ # If we are asked to return the (group of) transformation
968
+ # matrices to the user, we also require that those
969
+ # transformations act as the identity on the orthogonal
970
+ # complement of the space spanned by V. This complement is the
971
+ # space spanned by the columns of W = 1 - V V+. One can check
972
+ # that B = (V P V+) + W is the correct matrix: it acts the same
973
+ # as A on V and it satisfies B W = W.
974
+
975
+ outputs = ("abstract", "permutation", "matrix", "matrixlist")
976
+ if output not in outputs:
977
+ raise ValueError("unknown output {!r}, valid values are {}".format(output, outputs))
978
+
979
+ # For backwards compatibility, we treat "abstract" as
980
+ # "permutation", but where we add 1 to the indices of the
981
+ # permutations.
982
+ index0 = 0
983
+ if output == "abstract":
984
+ index0 = 1
985
+ output = "permutation"
986
+
987
+ if self.base_ring().is_exact():
988
+ def rational_approximation(c):
989
+ return c
990
+ else:
991
+ c_list = []
992
+
993
+ def rational_approximation(c):
994
+ # Implementation detail: Return unique integer if two
995
+ # c-values are the same up to machine precision. But
996
+ # you can think of it as a uniquely-chosen rational
997
+ # approximation.
998
+ for i, x in enumerate(c_list):
999
+ if self._is_zero(x - c):
1000
+ return i
1001
+ c_list.append(c)
1002
+ return len(c_list) - 1
1003
+
1004
+ if self.is_compact():
1005
+ def edge_label(i, j, c_ij):
1006
+ return c_ij
1007
+ else:
1008
+ # In the non-compact case, we also label the edges by the
1009
+ # type of the V-representation object. This ensures that
1010
+ # vertices, rays, and lines are only permuted amongst
1011
+ # themselves.
1012
+ def edge_label(i, j, c_ij):
1013
+ return (self.Vrepresentation(i).type(), c_ij, self.Vrepresentation(j).type())
1014
+
1015
+ # Homogeneous coordinates for the V-representation objects.
1016
+ # Mathematically, V is a matrix. For efficiency however, we
1017
+ # represent it as a list of column vectors.
1018
+ V = [v.homogeneous_vector() for v in self.Vrepresentation()]
1019
+
1020
+ # Pseudoinverse of V Vt
1021
+ Qplus = sum(v.column() * v.row() for v in V).pseudoinverse()
1022
+
1023
+ # Construct the graph.
1024
+ from sage.graphs.graph import Graph
1025
+ G = Graph()
1026
+ for i in range(len(V)):
1027
+ for j in range(i+1, len(V)):
1028
+ c_ij = rational_approximation(V[i] * Qplus * V[j])
1029
+ G.add_edge(index0+i, index0+j, edge_label(i, j, c_ij))
1030
+
1031
+ permgroup = G.automorphism_group(edge_labels=True)
1032
+ if output == "permutation":
1033
+ return permgroup
1034
+ elif output == "matrix":
1035
+ permgroup = permgroup.gens()
1036
+
1037
+ # Compute V+ = Vt Q+ as list of row vectors
1038
+ from sage.matrix.constructor import matrix
1039
+ Vplus = list(matrix(V) * Qplus) # matrix(V) is Vt
1040
+
1041
+ # Compute W = 1 - V V+
1042
+ W = 1 - sum(V[i].column() * Vplus[i].row() for i in range(len(V)))
1043
+
1044
+ # Convert the permutation group to a matrix group.
1045
+ # If P is a permutation, then we return the matrix
1046
+ # B = (V P V+) + W.
1047
+ #
1048
+ # If output == "matrix", we loop over the generators of the group.
1049
+ # Otherwise, we loop over all elements.
1050
+ matrices = []
1051
+ for perm in permgroup:
1052
+ A = sum(V[perm(i)].column() * Vplus[i].row() for i in range(len(V)))
1053
+ matrices.append(A + W)
1054
+
1055
+ for mat in matrices:
1056
+ mat.set_immutable()
1057
+
1058
+ if output == "matrixlist":
1059
+ return tuple(matrices)
1060
+ else:
1061
+ from sage.groups.matrix_gps.finitely_generated import MatrixGroup
1062
+ return MatrixGroup(matrices)
1063
+
1064
+ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'):
1065
+ r"""
1066
+ Return whether the polyhedron is combinatorially isomorphic to another polyhedron.
1067
+
1068
+ We only consider bounded polyhedra. By definition, they are
1069
+ combinatorially isomorphic if their face lattices are isomorphic.
1070
+
1071
+ INPUT:
1072
+
1073
+ - ``other`` -- a polyhedron object
1074
+ - ``algorithm`` -- (default: ``'bipartite_graph'``) the algorithm to
1075
+ use; the other possible value is ``'face_lattice'``
1076
+
1077
+ OUTPUT:
1078
+
1079
+ - ``True`` if the two polyhedra are combinatorially isomorphic
1080
+ - ``False`` otherwise
1081
+
1082
+ .. SEEALSO::
1083
+
1084
+ :meth:`combinatorial_automorphism_group`,
1085
+ :meth:`vertex_facet_graph`.
1086
+
1087
+ REFERENCES:
1088
+
1089
+ For the equivalence of the two algorithms see [KK1995]_, p. 877-878
1090
+
1091
+ EXAMPLES:
1092
+
1093
+ The square is combinatorially isomorphic to the 2-dimensional cube::
1094
+
1095
+ sage: polytopes.hypercube(2).is_combinatorially_isomorphic(polytopes.regular_polygon(4))
1096
+ True
1097
+
1098
+ All the faces of the 3-dimensional permutahedron are either
1099
+ combinatorially isomorphic to a square or a hexagon::
1100
+
1101
+ sage: H = polytopes.regular_polygon(6) # needs sage.rings.number_field
1102
+ sage: S = polytopes.hypercube(2)
1103
+ sage: P = polytopes.permutahedron(4)
1104
+ sage: all(F.as_polyhedron().is_combinatorially_isomorphic(S) # needs sage.rings.number_field
1105
+ ....: or F.as_polyhedron().is_combinatorially_isomorphic(H)
1106
+ ....: for F in P.faces(2))
1107
+ True
1108
+
1109
+ Checking that a regular simplex intersected with its reflection
1110
+ through the origin is combinatorially isomorphic to the intersection
1111
+ of a cube with a hyperplane perpendicular to its long diagonal::
1112
+
1113
+ sage: def simplex_intersection(k):
1114
+ ....: S1 = Polyhedron([vector(v)-vector(polytopes.simplex(k).center()) for v in polytopes.simplex(k).vertices_list()])
1115
+ ....: S2 = Polyhedron([-vector(v) for v in S1.vertices_list()])
1116
+ ....: return S1.intersection(S2)
1117
+ sage: def cube_intersection(k):
1118
+ ....: C = polytopes.hypercube(k+1)
1119
+ ....: H = Polyhedron(eqns=[[0]+[1 for i in range(k+1)]])
1120
+ ....: return C.intersection(H)
1121
+ sage: [simplex_intersection(k).is_combinatorially_isomorphic(cube_intersection(k)) for k in range(2,5)]
1122
+ [True, True, True]
1123
+ sage: simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # needs sage.rings.number_field
1124
+ True
1125
+ sage: simplex_intersection(3).is_combinatorially_isomorphic(polytopes.octahedron())
1126
+ True
1127
+
1128
+ Two polytopes with the same `f`-vector, but different combinatorial types::
1129
+
1130
+ sage: P = Polyhedron([[-605520/1525633, -605520/1525633, -1261500/1525633, -52200/1525633, 11833/1525633],\
1131
+ ....: [-720/1769, -600/1769, 1500/1769, 0, -31/1769], [-216/749, 240/749, -240/749, -432/749, 461/749], \
1132
+ ....: [-50/181, 50/181, 60/181, -100/181, -119/181], [-32/51, -16/51, -4/51, 12/17, 1/17],\
1133
+ ....: [1, 0, 0, 0, 0], [16/129, 128/129, 0, 0, 1/129], [64/267, -128/267, 24/89, -128/267, 57/89],\
1134
+ ....: [1200/3953, -1200/3953, -1440/3953, -360/3953, -3247/3953], [1512/5597, 1512/5597, 588/5597, 4704/5597, 2069/5597]])
1135
+ sage: C = polytopes.cyclic_polytope(5,10)
1136
+ sage: C.f_vector() == P.f_vector(); C.f_vector()
1137
+ True
1138
+ (1, 10, 45, 100, 105, 42, 1)
1139
+ sage: C.is_combinatorially_isomorphic(P)
1140
+ False
1141
+
1142
+ sage: S = polytopes.simplex(3)
1143
+ sage: S = S.face_truncation(S.faces(0)[3])
1144
+ sage: S = S.face_truncation(S.faces(0)[4])
1145
+ sage: S = S.face_truncation(S.faces(0)[5])
1146
+ sage: T = polytopes.simplex(3)
1147
+ sage: T = T.face_truncation(T.faces(0)[3])
1148
+ sage: T = T.face_truncation(T.faces(0)[4])
1149
+ sage: T = T.face_truncation(T.faces(0)[4])
1150
+ sage: T.is_combinatorially_isomorphic(S)
1151
+ False
1152
+ sage: T.f_vector(), S.f_vector()
1153
+ ((1, 10, 15, 7, 1), (1, 10, 15, 7, 1))
1154
+
1155
+ sage: C = polytopes.hypercube(5)
1156
+ sage: C.is_combinatorially_isomorphic(C)
1157
+ True
1158
+ sage: C.is_combinatorially_isomorphic(C, algorithm='magic')
1159
+ Traceback (most recent call last):
1160
+ ...
1161
+ AssertionError: `algorithm` must be 'bipartite graph' or 'face_lattice'
1162
+
1163
+ sage: G = Graph()
1164
+ sage: C.is_combinatorially_isomorphic(G)
1165
+ Traceback (most recent call last):
1166
+ ...
1167
+ AssertionError: input `other` must be a polyhedron
1168
+
1169
+ sage: H = Polyhedron(eqns=[[0,1,1,1,1]]); H
1170
+ A 3-dimensional polyhedron in QQ^4 defined as the convex hull of 1 vertex and 3 lines
1171
+ sage: C.is_combinatorially_isomorphic(H)
1172
+ Traceback (most recent call last):
1173
+ ...
1174
+ AssertionError: polyhedron `other` must be bounded
1175
+ """
1176
+ assert isinstance(other, Polyhedron_base4), "input `other` must be a polyhedron"
1177
+ assert self.is_compact(), "polyhedron `self` must be bounded"
1178
+ assert other.is_compact(), "polyhedron `other` must be bounded"
1179
+ assert algorithm in ['bipartite_graph', 'face_lattice'], "`algorithm` must be 'bipartite graph' or 'face_lattice'"
1180
+
1181
+ # For speed, we check if the polyhedra have the same number of facets and vertices.
1182
+ # This is faster than building the bipartite graphs first and
1183
+ # then check that they won't be isomorphic.
1184
+ if self.n_vertices() != other.n_vertices() or self.n_facets() != other.n_facets():
1185
+ return False
1186
+
1187
+ if algorithm == 'bipartite_graph':
1188
+ G_self = self.vertex_facet_graph(False)
1189
+ G_other = other.vertex_facet_graph(False)
1190
+
1191
+ return G_self.is_isomorphic(G_other)
1192
+ else:
1193
+ return self.face_lattice().is_isomorphic(other.face_lattice())
1194
+
1195
+ def _test_is_combinatorially_isomorphic(self, tester=None, **options):
1196
+ """
1197
+ Run tests on the method :meth:`.is_combinatorially_isomorphic`.
1198
+
1199
+ TESTS::
1200
+
1201
+ sage: polytopes.cross_polytope(3)._test_is_combinatorially_isomorphic()
1202
+ """
1203
+ if tester is None:
1204
+ tester = self._tester(**options)
1205
+
1206
+ if not self.is_compact():
1207
+ with tester.assertRaises(AssertionError):
1208
+ self.is_combinatorially_isomorphic(self)
1209
+ return
1210
+
1211
+ if self.n_vertices() > 200 or self.n_facets() > 200:
1212
+ # Avoid very long doctests.
1213
+ return
1214
+
1215
+ try:
1216
+ import sage.graphs.graph
1217
+ except ImportError:
1218
+ return
1219
+
1220
+ from sage.rings.integer_ring import ZZ
1221
+ tester.assertTrue(self.is_combinatorially_isomorphic(ZZ(4)*self))
1222
+ if self.n_vertices():
1223
+ tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center()))
1224
+
1225
+ if self.n_vertices() < 20 and self.n_facets() < 20 and self.is_immutable():
1226
+ tester.assertTrue(self.is_combinatorially_isomorphic(ZZ(4)*self, algorithm='face_lattice'))
1227
+ if self.n_vertices():
1228
+ tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center(), algorithm='face_lattice'))
1229
+
1230
+ def is_self_dual(self):
1231
+ r"""
1232
+ Return whether the polytope is self-dual.
1233
+
1234
+ A polytope is self-dual if its face lattice is isomorphic to the face
1235
+ lattice of its dual polytope.
1236
+
1237
+ EXAMPLES::
1238
+
1239
+ sage: polytopes.simplex().is_self_dual()
1240
+ True
1241
+ sage: polytopes.twenty_four_cell().is_self_dual()
1242
+ True
1243
+ sage: polytopes.cube().is_self_dual()
1244
+ False
1245
+ sage: polytopes.hypersimplex(5,2).is_self_dual() # needs sage.combinat
1246
+ False
1247
+ sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]).is_self_dual()
1248
+ Traceback (most recent call last):
1249
+ ...
1250
+ ValueError: polyhedron has to be compact
1251
+ """
1252
+ if not self.is_compact():
1253
+ raise ValueError("polyhedron has to be compact")
1254
+
1255
+ n = self.n_vertices()
1256
+ m = self.n_facets()
1257
+ if n != m:
1258
+ return False
1259
+
1260
+ G1 = self.vertex_facet_graph()
1261
+ G2 = G1.reverse()
1262
+ return G1.is_isomorphic(G2)