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,3859 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ r"""
3
+ Combinatorial polyhedron
4
+
5
+ This module gathers algorithms for polyhedra that only depend on the
6
+ vertex-facet incidences and that are called combinatorial polyhedron.
7
+ The main class is :class:`CombinatorialPolyhedron`. Most importantly,
8
+ this class allows to iterate quickly through the faces (possibly
9
+ of given dimension) via the :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator` object. The :class:`CombinatorialPolyhedron`
10
+ uses this iterator to quickly generate the f-vector, the edges,
11
+ the ridges and the face lattice.
12
+
13
+ Terminology used in this module:
14
+
15
+ - Vrep -- ``[vertices, rays, lines]`` of the polyhedron
16
+ - Hrep -- inequalities and equations of the polyhedron
17
+ - Facets -- facets of the polyhedron
18
+ - Vrepresentation -- represents a face by the list of Vrep it contains
19
+ - Hrepresentation -- represents a face by a list of Hrep it is contained in
20
+ - bit representation -- represents incidences as bitset, where each bit
21
+ represents one incidence. There might be trailing zeros, to fit alignment
22
+ requirements. In most instances, faces are represented by the bit
23
+ representation, where each bit corresponds to a Vrep or facet. Thus a bit
24
+ representation can either be a Vrep or facet representation depending on
25
+ context.
26
+
27
+ EXAMPLES:
28
+
29
+ Construction::
30
+
31
+ sage: P = polytopes.hypercube(4)
32
+ sage: C = CombinatorialPolyhedron(P); C
33
+ A 4-dimensional combinatorial polyhedron with 8 facets
34
+
35
+ Obtaining edges and ridges::
36
+
37
+ sage: C.edges()[:2]
38
+ ((A vertex at (1, -1, -1, -1), A vertex at (-1, -1, -1, -1)),
39
+ (A vertex at (-1, -1, -1, 1), A vertex at (-1, -1, -1, -1)))
40
+ sage: C.edges(names=False)[:2]
41
+ ((6, 15), (14, 15))
42
+
43
+ sage: C.ridges()[:2]
44
+ ((An inequality (0, 0, 1, 0) x + 1 >= 0,
45
+ An inequality (0, 1, 0, 0) x + 1 >= 0),
46
+ (An inequality (0, 0, 0, 1) x + 1 >= 0,
47
+ An inequality (0, 1, 0, 0) x + 1 >= 0))
48
+ sage: C.ridges(names=False)[:2]
49
+ ((6, 7), (5, 7))
50
+
51
+ Vertex-graph and facet-graph::
52
+
53
+ sage: C.vertex_graph() # needs sage.graphs
54
+ Graph on 16 vertices
55
+ sage: C.facet_graph() # needs sage.graphs
56
+ Graph on 8 vertices
57
+
58
+ Face lattice::
59
+
60
+ sage: C.face_lattice() # needs sage.combinat
61
+ Finite lattice containing 82 elements
62
+
63
+ Face iterator::
64
+
65
+ sage: C.face_generator()
66
+ Iterator over the proper faces of a 4-dimensional combinatorial polyhedron
67
+
68
+ sage: C.face_generator(2)
69
+ Iterator over the 2-faces of a 4-dimensional combinatorial polyhedron
70
+
71
+ AUTHOR:
72
+
73
+ - Jonathan Kliem (2019-04)
74
+ """
75
+
76
+ # ****************************************************************************
77
+ # Copyright (C) 2019 Jonathan Kliem <jonathan.kliem@gmail.com>
78
+ #
79
+ # This program is free software: you can redistribute it and/or modify
80
+ # it under the terms of the GNU General Public License as published by
81
+ # the Free Software Foundation, either version 2 of the License, or
82
+ # (at your option) any later version.
83
+ # https://www.gnu.org/licenses/
84
+ # ****************************************************************************
85
+
86
+ import numbers
87
+ from memory_allocator cimport MemoryAllocator
88
+ from cysignals.memory cimport check_calloc, sig_free
89
+
90
+ import sage.geometry.abc
91
+
92
+ from sage.matrix.matrix_dense cimport Matrix_dense
93
+ from sage.misc.misc import is_iterator
94
+ from sage.structure.element import Matrix
95
+ from .conversions import (incidence_matrix_to_bit_rep_of_facets,
96
+ incidence_matrix_to_bit_rep_of_Vrep,
97
+ facets_tuple_to_bit_rep_of_facets,
98
+ facets_tuple_to_bit_rep_of_Vrep)
99
+ from sage.geometry.polyhedron.combinatorial_polyhedron.conversions cimport Vrep_list_to_bit_rep
100
+ from sage.misc.cachefunc import cached_method
101
+
102
+ from sage.rings.integer cimport smallInteger
103
+ from cysignals.signals cimport sig_check
104
+
105
+ from sage.geometry.polyhedron.combinatorial_polyhedron.face_data_structure cimport face_len_atoms, face_init, face_free
106
+ from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator cimport iter_t, parallel_f_vector
107
+
108
+
109
+ cdef extern from "Python.h":
110
+ int unlikely(int) nogil # Defined by Cython
111
+
112
+
113
+ cdef class CombinatorialPolyhedron(SageObject):
114
+ r"""
115
+ The class of the Combinatorial Type of a Polyhedron, a Polytope.
116
+
117
+ INPUT:
118
+
119
+ - ``data`` -- an instance of
120
+ * :class:`~sage.geometry.polyhedron.parent.Polyhedron_base`
121
+ * or a :class:`~sage.geometry.lattice_polytope.LatticePolytopeClass`
122
+ * or a :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone`
123
+ * or an ``incidence_matrix`` as in
124
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix`
125
+ In this case you should also specify the ``Vrep`` and ``facets`` arguments
126
+ * or list of facets, each facet given as
127
+ a list of ``[vertices, rays, lines]`` if the polyhedron is unbounded,
128
+ then rays and lines and the extra argument ``nr_lines`` are required
129
+ if the polyhedron contains no lines, the rays can be thought of
130
+ as the vertices of the facets deleted from a bounded polyhedron see
131
+ :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` on how to use
132
+ rays and lines
133
+ * or an integer, representing the dimension of a polyhedron equal to its
134
+ affine hull
135
+ * or a tuple consisting of facets and vertices as two
136
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`.
137
+ - ``Vrep`` -- (optional) when ``data`` is an incidence matrix, it should
138
+ be the list of ``[vertices, rays, lines]``, if the rows in the incidence_matrix
139
+ should correspond to names
140
+ - ``facets`` -- (optional) when ``data`` is an incidence matrix or a list of facets,
141
+ it should be a list of facets that would be used instead of indices (of the columns
142
+ of the incidence matrix).
143
+ - ``unbounded`` -- value will be overwritten if ``data`` is a polyhedron;
144
+ if ``unbounded`` and ``data`` is incidence matrix or a list of facets,
145
+ need to specify ``far_face``
146
+ - ``far_face`` -- (semi-optional); if the polyhedron is unbounded this
147
+ needs to be set to the list of indices of the rays and line unless ``data`` is
148
+ an instance of :class:`~sage.geometry.polyhedron.parent.Polyhedron_base`.
149
+
150
+ EXAMPLES:
151
+
152
+ We illustrate all possible input: a polyhedron:
153
+
154
+ sage: P = polytopes.cube()
155
+ sage: CombinatorialPolyhedron(P)
156
+ A 3-dimensional combinatorial polyhedron with 6 facets
157
+
158
+ a lattice polytope::
159
+
160
+ sage: points = [(1,0,0), (0,1,0), (0,0,1),
161
+ ....: (-1,0,0), (0,-1,0), (0,0,-1)]
162
+ sage: L = LatticePolytope(points)
163
+ sage: CombinatorialPolyhedron(L)
164
+ A 3-dimensional combinatorial polyhedron with 8 facets
165
+
166
+ a cone::
167
+
168
+ sage: M = Cone([(1,0), (0,1)])
169
+ sage: CombinatorialPolyhedron(M)
170
+ A 2-dimensional combinatorial polyhedron with 2 facets
171
+
172
+ an incidence matrix::
173
+
174
+ sage: P = Polyhedron(rays=[[0,1]])
175
+ sage: data = P.incidence_matrix()
176
+ sage: far_face = [i for i in range(2) if not P.Vrepresentation()[i].is_vertex()]
177
+ sage: CombinatorialPolyhedron(data, unbounded=True, far_face=far_face)
178
+ A 1-dimensional combinatorial polyhedron with 1 facet
179
+ sage: C = CombinatorialPolyhedron(data, Vrep=['myvertex'],
180
+ ....: facets=['myfacet'], unbounded=True, far_face=far_face)
181
+ sage: C.Vrepresentation()
182
+ ('myvertex',)
183
+ sage: C.Hrepresentation()
184
+ ('myfacet',)
185
+
186
+ a list of facets::
187
+
188
+ sage: CombinatorialPolyhedron(((1,2,3),(1,2,4),(1,3,4),(2,3,4)))
189
+ A 3-dimensional combinatorial polyhedron with 4 facets
190
+ sage: facetnames = ['facet0', 'facet1', 'facet2', 'myfacet3']
191
+ sage: facetinc = ((1,2,3),(1,2,4),(1,3,4),(2,3,4))
192
+ sage: C = CombinatorialPolyhedron(facetinc, facets=facetnames)
193
+ sage: C.Vrepresentation()
194
+ (1, 2, 3, 4)
195
+ sage: C.Hrepresentation()
196
+ ('facet0', 'facet1', 'facet2', 'myfacet3')
197
+
198
+ an integer::
199
+
200
+ sage: CombinatorialPolyhedron(-1).f_vector()
201
+ (1)
202
+ sage: CombinatorialPolyhedron(0).f_vector()
203
+ (1, 1)
204
+ sage: CombinatorialPolyhedron(5).f_vector()
205
+ (1, 0, 0, 0, 0, 0, 1)
206
+
207
+ tuple of ``ListOfFaces``::
208
+
209
+ sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \
210
+ ....: import facets_tuple_to_bit_rep_of_facets, \
211
+ ....: facets_tuple_to_bit_rep_of_Vrep
212
+ sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4),
213
+ ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5))
214
+ sage: facets = facets_tuple_to_bit_rep_of_facets(bi_pyr, 6)
215
+ sage: Vrep = facets_tuple_to_bit_rep_of_Vrep(bi_pyr, 6)
216
+ sage: C = CombinatorialPolyhedron((facets, Vrep)); C
217
+ A 3-dimensional combinatorial polyhedron with 8 facets
218
+ sage: C.f_vector()
219
+ (1, 6, 12, 8, 1)
220
+
221
+ Specifying that a polyhedron is unbounded is important. The following with a
222
+ polyhedron works fine::
223
+
224
+ sage: P = Polyhedron(ieqs=[[1,-1,0],[1,1,0]])
225
+ sage: C = CombinatorialPolyhedron(P) # this works fine
226
+ sage: C
227
+ A 2-dimensional combinatorial polyhedron with 2 facets
228
+
229
+ The following is incorrect, as ``unbounded`` is implicitly set to ``False``::
230
+
231
+ sage: data = P.incidence_matrix()
232
+ sage: vert = P.Vrepresentation()
233
+ sage: C = CombinatorialPolyhedron(data, Vrep=vert)
234
+ sage: C
235
+ A 2-dimensional combinatorial polyhedron with 2 facets
236
+ sage: C.f_vector()
237
+ Traceback (most recent call last):
238
+ ...
239
+ ValueError: not all vertices are intersections of facets
240
+ sage: C.vertices()
241
+ (A line in the direction (0, 1), A vertex at (1, 0), A vertex at (-1, 0))
242
+
243
+ The correct usage is::
244
+
245
+ sage: far_face = [i for i in range(3) if not P.Vrepresentation()[i].is_vertex()]
246
+ sage: C = CombinatorialPolyhedron(data, Vrep=vert, unbounded=True, far_face=far_face)
247
+ sage: C
248
+ A 2-dimensional combinatorial polyhedron with 2 facets
249
+ sage: C.f_vector()
250
+ (1, 0, 2, 1)
251
+ sage: C.vertices()
252
+ ()
253
+
254
+ TESTS:
255
+
256
+ Checking that :issue:`27987` is fixed::
257
+
258
+ sage: P1 = Polyhedron(vertices=[[0,1],[1,0]], rays=[[1,1]])
259
+ sage: P2 = Polyhedron(vertices=[[0,1],[1,0],[1,1]])
260
+ sage: P1.incidence_matrix() == P2.incidence_matrix()
261
+ True
262
+ sage: CombinatorialPolyhedron(P1).f_vector()
263
+ (1, 2, 3, 1)
264
+ sage: CombinatorialPolyhedron(P2).f_vector()
265
+ (1, 3, 3, 1)
266
+ sage: P1 = Polyhedron(vertices=[[0,1],[1,0]], rays=[[1,1]])
267
+ sage: P2 = Polyhedron(vertices=[[0,1],[1,0],[1,1]])
268
+ sage: CombinatorialPolyhedron(P1).f_vector()
269
+ (1, 2, 3, 1)
270
+ sage: CombinatorialPolyhedron(P2).f_vector()
271
+ (1, 3, 3, 1)
272
+
273
+ Some other tests regarding small polyhedra::
274
+
275
+ sage: P = Polyhedron(rays=[[1,0],[0,1]])
276
+ sage: C = CombinatorialPolyhedron(P)
277
+ sage: C
278
+ A 2-dimensional combinatorial polyhedron with 2 facets
279
+ sage: C.f_vector()
280
+ (1, 1, 2, 1)
281
+ sage: C.vertices()
282
+ (A vertex at (0, 0),)
283
+ sage: data = P.incidence_matrix()
284
+ sage: vert = P.Vrepresentation()
285
+ sage: far_face = [i for i in range(3) if not P.Vrepresentation()[i].is_vertex()]
286
+ sage: C = CombinatorialPolyhedron(data, Vrep=vert, unbounded=True, far_face=far_face)
287
+ sage: C
288
+ A 2-dimensional combinatorial polyhedron with 2 facets
289
+ sage: C.f_vector()
290
+ (1, 1, 2, 1)
291
+ sage: C.vertices()
292
+ (A vertex at (0, 0),)
293
+ sage: CombinatorialPolyhedron(3r)
294
+ A 3-dimensional combinatorial polyhedron with 0 facets
295
+
296
+ Check that on wrong input subsequent calls of ``f_vector`` fail::
297
+
298
+ sage: data = P.incidence_matrix()
299
+ sage: vert = P.Vrepresentation()
300
+ sage: C = CombinatorialPolyhedron(data, Vrep=vert)
301
+ sage: C.f_vector()
302
+ Traceback (most recent call last):
303
+ ...
304
+ ValueError: not all vertices are intersections of facets
305
+ sage: C.f_vector()
306
+ Traceback (most recent call last):
307
+ ...
308
+ ValueError: not all vertices are intersections of facets
309
+
310
+ Check that :issue:`28678` is fixed::
311
+
312
+ sage: CombinatorialPolyhedron([])
313
+ A -1-dimensional combinatorial polyhedron with 0 facets
314
+ sage: CombinatorialPolyhedron(LatticePolytope([], lattice=ToricLattice(3)))
315
+ A -1-dimensional combinatorial polyhedron with 0 facets
316
+ """
317
+ def __cinit__(self):
318
+ r"""
319
+ TESTS:
320
+
321
+ Not initializing the class, does not give segmentation fault::
322
+
323
+ sage: from sage.geometry.polyhedron.combinatorial_polyhedron.base import CombinatorialPolyhedron
324
+ sage: C = CombinatorialPolyhedron.__new__(CombinatorialPolyhedron)
325
+ sage: C.f_vector()
326
+ Traceback (most recent call last):
327
+ ...
328
+ ValueError: the combinatorial polyhedron was not initialized
329
+ sage: C.face_lattice() # needs sage.combinat
330
+ Traceback (most recent call last):
331
+ ...
332
+ ValueError: the combinatorial polyhedron was not initialized
333
+ sage: C.face_generator()
334
+ Traceback (most recent call last):
335
+ ...
336
+ ValueError: the combinatorial polyhedron was not initialized
337
+ """
338
+ # Note that all values are set to zero at the time ``__cinit__`` is called:
339
+ # https://cython.readthedocs.io/en/latest/src/userguide/special_methods.html#initialisation-methods
340
+ # In particular, ``__dealloc__`` will not do harm in this case.
341
+
342
+ self._dimension = -2 # a "NULL" value
343
+ self._equations = ()
344
+ self._all_faces = None
345
+ self._n_facets = -1
346
+
347
+ def __init__(self, data, Vrep=None, facets=None, unbounded=False, far_face=None):
348
+ r"""
349
+ Initialize :class:`CombinatorialPolyhedron`.
350
+
351
+ See :class:`CombinatorialPolyhedron`.
352
+
353
+ TESTS::
354
+
355
+ sage: C = CombinatorialPolyhedron([[0,1,2], [0,1,3], # indirect doctest
356
+ ....: [0,2,3], [1,2,3]])
357
+
358
+ sage: TestSuite(sage.geometry.polyhedron.combinatorial_polyhedron.base.CombinatorialPolyhedron).run()
359
+ """
360
+ self._equations = ()
361
+ self._far_face_tuple = ()
362
+
363
+ if isinstance(data, sage.geometry.abc.Polyhedron):
364
+ self._init_from_polyhedron(data)
365
+ return
366
+ if isinstance(data, sage.geometry.abc.LatticePolytope):
367
+ self._init_from_lattice_polytope(data)
368
+ return
369
+ if isinstance(data, sage.geometry.abc.ConvexRationalPolyhedralCone):
370
+ self._init_from_cone(data)
371
+ return
372
+
373
+ self._bounded = not unbounded
374
+ if unbounded:
375
+ if not far_face:
376
+ raise ValueError("must specify far face for unbounded polyhedron")
377
+ self._far_face_tuple = tuple(far_face)
378
+
379
+ if Vrep:
380
+ self._Vrep = tuple(Vrep)
381
+
382
+ self._init_facet_names(facets)
383
+
384
+ if data == [] or data == ():
385
+ self._init_as_trivial_polyhedron(-1)
386
+ elif isinstance(data, Matrix):
387
+ self._init_from_incidence_matrix(data)
388
+ elif isinstance(data, numbers.Integral):
389
+ self._init_as_trivial_polyhedron(data)
390
+ elif (isinstance(data, (tuple, list)) and
391
+ len(data) == 2 and
392
+ isinstance(data[0], ListOfFaces) and
393
+ isinstance(data[1], ListOfFaces)):
394
+ self._init_from_ListOfFaces(data[0], data[1])
395
+
396
+ else:
397
+ self._init_from_list_of_facets(data)
398
+
399
+ cdef _init_from_polyhedron(self, data):
400
+ r'''
401
+ Initialize from :class:`~sage.geometry.polyhedron.parent.Polyhedron_base`.
402
+ '''
403
+ self._Vrep = data.Vrepresentation()
404
+ self._facet_names = data.inequalities()
405
+ self._equations = data.equations()
406
+ self._dimension = data.dimension()
407
+
408
+ if not data.is_compact():
409
+ self._bounded = False
410
+ self._far_face_tuple = tuple(i for i in range(data.n_Vrepresentation()) if not data.Vrepresentation()[i].is_vertex())
411
+ else:
412
+ self._bounded = True
413
+
414
+ return self._init_from_incidence_matrix(data.incidence_matrix())
415
+
416
+ cdef _init_from_lattice_polytope(self, data):
417
+ r'''
418
+ Initialize from :class:`~sage.geometry.lattice_polytope.LatticePolytopeClass`.
419
+ '''
420
+ self._bounded = True
421
+ self._Vrep = tuple(data.vertices())
422
+ self._facet_names = tuple(data.facet_normals())
423
+ self._dimension = data.dimension()
424
+ return self._init_from_incidence_matrix(data.incidence_matrix())
425
+
426
+ cdef _init_from_cone(self, data):
427
+ r'''
428
+ Initialize from :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone`.
429
+ '''
430
+ self._bounded = False
431
+ self._Vrep = tuple(data.rays()) + (data.lattice().zero(),)
432
+ self._facet_names = tuple(data.facet_normals())
433
+ self._far_face_tuple = tuple(i for i in range(len(self._Vrep) - 1))
434
+ self._dimension = data.dim()
435
+ from sage.matrix.constructor import matrix
436
+ from sage.rings.integer_ring import ZZ
437
+ incidence_matrix = matrix(ZZ, data.incidence_matrix().rows()
438
+ + [[ZZ.one() for _ in range(len(data.facet_normals()))]])
439
+ return self._init_from_incidence_matrix(incidence_matrix)
440
+
441
+ cdef _init_facet_names(self, facets):
442
+ '''
443
+ Store facet names and compute equations.
444
+ '''
445
+ if facets is not None:
446
+ facets = tuple(facets)
447
+
448
+ test = [1] * len(facets) # 0 if that facet is an equation
449
+ for i in range(len(facets)):
450
+ if hasattr(facets[i], "is_inequality"):
451
+ # We remove equations.
452
+ # At the moment only equations with this attribute ``True``
453
+ # will be detected.
454
+ if not facets[i].is_inequality():
455
+ test[i] = 0
456
+ self._facet_names = tuple(facets[i] for i in range(len(facets)) if test[i])
457
+
458
+ self._equations = tuple(facets[i] for i in range(len(facets)) if not test[i])
459
+ else:
460
+ self._facet_names = None
461
+
462
+ cdef _init_from_incidence_matrix(self, data):
463
+ """
464
+ Initialize from an incidence matrix.
465
+ """
466
+ # Input is incidence-matrix or was converted to it.
467
+ self._n_Hrepresentation = data.ncols()
468
+ self._n_Vrepresentation = data.nrows()
469
+
470
+ if not isinstance(data, Matrix_dense):
471
+ from sage.rings.integer_ring import ZZ
472
+ from sage.matrix.constructor import matrix
473
+ data = matrix(ZZ, data, sparse=False)
474
+ assert isinstance(data, Matrix_dense), "conversion to ``Matrix_dense`` didn't work"
475
+
476
+ # Store the incidence matrix.
477
+ if not data.is_immutable():
478
+ data = data.__copy__()
479
+ data.set_immutable()
480
+ self.incidence_matrix.set_cache(data)
481
+
482
+ # Delete equations.
483
+ data = data.delete_columns(
484
+ [i for i in range(data.ncols())
485
+ if all(data[j, i] for j in range(data.nrows()))],
486
+ check=False)
487
+
488
+ # Initializing the facets in their Bit-representation.
489
+ self._bitrep_facets = incidence_matrix_to_bit_rep_of_facets(data)
490
+
491
+ # Initializing the Vrep as their Bit-representation.
492
+ self._bitrep_Vrep = incidence_matrix_to_bit_rep_of_Vrep(data)
493
+
494
+ self._n_facets = self.bitrep_facets().n_faces()
495
+
496
+ self._initialize_far_face()
497
+
498
+ cdef _init_from_list_of_facets(self, data):
499
+ """
500
+ Initialize from a list of facets.
501
+
502
+ Tuple and iterator work as well.
503
+
504
+ The facets are given by its ``[vertices, rays, lines]``.
505
+ """
506
+ if is_iterator(data):
507
+ data = tuple(data)
508
+
509
+ if self._Vrep is None:
510
+ # Get the names of the Vrep.
511
+ Vrep = sorted(set.union(*map(set, data)))
512
+ n_Vrepresentation = len(Vrep)
513
+ if Vrep != range(len(Vrep)):
514
+ self._Vrep = tuple(Vrep)
515
+ Vinv = {v: i for i, v in enumerate(self._Vrep)}
516
+ else:
517
+ # Assuming the user gave as correct names for the vertices
518
+ # and labeled them instead by `0,...,n`.
519
+ n_Vrepresentation = len(self._Vrep)
520
+
521
+ self._n_Vrepresentation = n_Vrepresentation
522
+
523
+ # Relabel the Vrep to be `0,...,n`.
524
+ if self._Vrep is not None:
525
+ def f(v):
526
+ return Vinv[v]
527
+ else:
528
+ def f(v):
529
+ return int(v)
530
+ facets = tuple(tuple(f(i) for i in j) for j in data)
531
+
532
+ self._n_facets = len(facets)
533
+ self._n_Hrepresentation = len(facets)
534
+
535
+ # Initializing the facets in their Bit-representation.
536
+ self._bitrep_facets = facets_tuple_to_bit_rep_of_facets(facets, n_Vrepresentation)
537
+
538
+ # Initializing the Vrep as their Bit-representation.
539
+ self._bitrep_Vrep = facets_tuple_to_bit_rep_of_Vrep(facets, n_Vrepresentation)
540
+
541
+ self._initialize_far_face()
542
+
543
+ cdef _init_from_ListOfFaces(self, ListOfFaces facets, ListOfFaces Vrep):
544
+ """
545
+ Initialize ``self`` from two ``ListOfFaces``.
546
+ """
547
+ self._bitrep_facets = facets
548
+ self._bitrep_Vrep = Vrep
549
+
550
+ self._n_Hrepresentation = self._bitrep_facets.n_faces()
551
+ self._n_Vrepresentation = self._bitrep_Vrep.n_faces()
552
+ self._n_facets = self._n_Hrepresentation
553
+
554
+ self._initialize_far_face()
555
+
556
+ cdef _initialize_far_face(self):
557
+ """
558
+ Initialize far_face if unbounded.
559
+ """
560
+ if not self._bounded:
561
+ face_init(self._far_face, self.bitrep_facets().n_atoms(), self._n_facets)
562
+ Vrep_list_to_bit_rep(tuple(self._far_face_tuple), self._far_face)
563
+
564
+ cdef _init_as_trivial_polyhedron(self, int dimension):
565
+ """
566
+ Initialize polyhedron equal to its affine hull.
567
+ """
568
+ if dimension < -1:
569
+ raise ValueError("any polyhedron must have dimension at least -1")
570
+ self._dimension = dimension
571
+
572
+ if self._dimension == 0:
573
+ self._n_facets = 1
574
+ self._n_Vrepresentation = 1
575
+ else:
576
+ self._n_facets = 0
577
+ self._n_Vrepresentation = 0
578
+
579
+ # Initializing the facets in their Bit-representation.
580
+ self._bitrep_facets = facets_tuple_to_bit_rep_of_facets((), 0)
581
+
582
+ # Initializing the Vrep as their Bit-representation.
583
+ self._bitrep_Vrep = facets_tuple_to_bit_rep_of_Vrep((), 0)
584
+
585
+ def __dealloc__(self):
586
+ """
587
+ TESTS::
588
+
589
+ sage: CombinatorialPolyhedron(-2) # indirect doctest
590
+ Traceback (most recent call last):
591
+ ...
592
+ ValueError: any polyhedron must have dimension at least -1
593
+ """
594
+ if not self._bounded:
595
+ face_free(self._far_face)
596
+
597
+ def _repr_(self):
598
+ r"""
599
+ Return a description of the combinatorial polyhedron.
600
+
601
+ EXAMPLES::
602
+
603
+ sage: P = polytopes.simplex()
604
+ sage: C = CombinatorialPolyhedron(P)
605
+ sage: C._repr_()
606
+ 'A 3-dimensional combinatorial polyhedron with 4 facets'
607
+
608
+ sage: P = Polyhedron(vertices=[])
609
+ sage: C = CombinatorialPolyhedron(P)
610
+ sage: C._repr_()
611
+ 'A -1-dimensional combinatorial polyhedron with 0 facets'
612
+
613
+ sage: P = Polyhedron(vertices=[[0,0]])
614
+ sage: C = CombinatorialPolyhedron(P)
615
+ sage: C._repr_()
616
+ 'A 0-dimensional combinatorial polyhedron with 0 facets'
617
+
618
+ sage: P = Polyhedron(lines=[[0,0,1],[0,1,0]])
619
+ sage: C = CombinatorialPolyhedron(P)
620
+ sage: C._repr_()
621
+ 'A 2-dimensional combinatorial polyhedron with 0 facets'
622
+
623
+ sage: P = Polyhedron(rays=[[1,0,0],[0,1,0],[-1,0,0]])
624
+ sage: C = CombinatorialPolyhedron(P)
625
+ sage: C._repr_()
626
+ 'A 2-dimensional combinatorial polyhedron with 1 facet'
627
+ """
628
+ desc = "A {}-dimensional combinatorial polyhedron with {} facet"\
629
+ .format(self.dimension(), self.n_facets())
630
+ if self.n_facets() != 1:
631
+ desc += "s"
632
+ return desc
633
+
634
+ def __reduce__(self):
635
+ r"""
636
+ Override __reduce__ to correctly pickle/unpickle.
637
+
638
+ TESTS::
639
+
640
+ sage: # needs sage.combinat
641
+ sage: P = polytopes.permutahedron(4)
642
+ sage: C = CombinatorialPolyhedron(P)
643
+ sage: C1 = loads(C.dumps())
644
+ sage: it = C.face_generator()
645
+ sage: it1 = C1.face_generator()
646
+ sage: tup = tuple((face.ambient_Vrepresentation(),
647
+ ....: face.ambient_Hrepresentation()) for face in it)
648
+ sage: tup1 = tuple((face.ambient_Vrepresentation(),
649
+ ....: face.ambient_Hrepresentation()) for face in it1)
650
+ sage: tup == tup1
651
+ True
652
+
653
+ sage: P = polytopes.cyclic_polytope(4,10)
654
+ sage: C = CombinatorialPolyhedron(P)
655
+ sage: C1 = loads(C.dumps())
656
+ sage: it = C.face_generator()
657
+ sage: it1 = C1.face_generator()
658
+ sage: tup = tuple((face.ambient_Vrepresentation(), face.ambient_Hrepresentation()) for face in it)
659
+ sage: tup1 = tuple((face.ambient_Vrepresentation(), face.ambient_Hrepresentation()) for face in it1)
660
+ sage: tup == tup1
661
+ True
662
+
663
+ sage: P = Polyhedron(rays=[[1,0,0], [-1,0,0], [0,-1,0]])
664
+ sage: C = CombinatorialPolyhedron(P)
665
+ sage: C1 = loads(C.dumps())
666
+ sage: it = C.face_generator()
667
+ sage: it1 = C1.face_generator()
668
+ sage: tup = tuple((face.ambient_Vrepresentation(), face.ambient_Hrepresentation()) for face in it)
669
+ sage: tup1 = tuple((face.ambient_Vrepresentation(), face.ambient_Hrepresentation()) for face in it1)
670
+ sage: tup == tup1
671
+ True
672
+
673
+ sage: P = Polyhedron(rays=[[1,0,0], [-1,0,0],
674
+ ....: [0,-1,0], [0,1,0]])
675
+ sage: C = CombinatorialPolyhedron(P)
676
+ sage: C1 = loads(C.dumps())
677
+ sage: it = C.face_generator()
678
+ sage: it1 = C1.face_generator()
679
+ sage: tup = tuple((face.ambient_Vrepresentation(), face.ambient_Hrepresentation()) for face in it)
680
+ sage: tup1 = tuple((face.ambient_Vrepresentation(), face.ambient_Hrepresentation()) for face in it1)
681
+ sage: tup == tup1
682
+ True
683
+ """
684
+ # Give a constructor by list of facets.
685
+ if not self.is_bounded():
686
+ return (CombinatorialPolyhedron, (self.incidence_matrix(),
687
+ self.Vrepresentation(), self.Hrepresentation(),
688
+ True, self.far_face_tuple()))
689
+ else:
690
+ return (CombinatorialPolyhedron, (self.incidence_matrix(),
691
+ self.Vrepresentation(), self.Hrepresentation()))
692
+
693
+ def _test_bitsets(self, tester=None, **options):
694
+ """
695
+ Test if the bitsets are consistent.
696
+
697
+ TESTS::
698
+
699
+ sage: P = polytopes.cube()
700
+ sage: C = CombinatorialPolyhedron(P)
701
+ sage: C._test_bitsets()
702
+ """
703
+ if tester is None:
704
+ tester = self._tester(**options)
705
+
706
+ cdef ListOfFaces facets = self.bitrep_facets()
707
+ cdef ListOfFaces Vrep = self.bitrep_Vrep()
708
+
709
+ tester.assertEqual(facets.matrix(), Vrep.matrix().transpose())
710
+
711
+ def Vrepresentation(self):
712
+ r"""
713
+ Return a list of names of ``[vertices, rays, lines]``.
714
+
715
+ EXAMPLES::
716
+
717
+ sage: P = Polyhedron(rays=[[1,0,0], [0,1,0], \
718
+ ....: [0,0,1],[0,0,-1]])
719
+ sage: C = CombinatorialPolyhedron(P)
720
+ sage: C.Vrepresentation()
721
+ (A line in the direction (0, 0, 1),
722
+ A ray in the direction (1, 0, 0),
723
+ A vertex at (0, 0, 0),
724
+ A ray in the direction (0, 1, 0))
725
+
726
+ sage: points = [(1,0,0), (0,1,0), (0,0,1),
727
+ ....: (-1,0,0), (0,-1,0), (0,0,-1)]
728
+ sage: L = LatticePolytope(points)
729
+ sage: C = CombinatorialPolyhedron(L)
730
+ sage: C.Vrepresentation()
731
+ (M(1, 0, 0), M(0, 1, 0), M(0, 0, 1), M(-1, 0, 0), M(0, -1, 0), M(0, 0, -1))
732
+
733
+ sage: M = Cone([(1,0), (0,1)])
734
+ sage: CombinatorialPolyhedron(M).Vrepresentation()
735
+ (N(1, 0), N(0, 1), N(0, 0))
736
+ """
737
+ if self.Vrep() is not None:
738
+ return self.Vrep()
739
+ else:
740
+ return tuple(smallInteger(i) for i in range(self.n_Vrepresentation()))
741
+
742
+ def Hrepresentation(self):
743
+ r"""
744
+ Return a list of names of facets and possibly some equations.
745
+
746
+ EXAMPLES::
747
+
748
+ sage: P = polytopes.permutahedron(3)
749
+ sage: C = CombinatorialPolyhedron(P)
750
+ sage: C.Hrepresentation()
751
+ (An inequality (1, 1, 0) x - 3 >= 0,
752
+ An inequality (-1, -1, 0) x + 5 >= 0,
753
+ An inequality (0, 1, 0) x - 1 >= 0,
754
+ An inequality (-1, 0, 0) x + 3 >= 0,
755
+ An inequality (1, 0, 0) x - 1 >= 0,
756
+ An inequality (0, -1, 0) x + 3 >= 0,
757
+ An equation (1, 1, 1) x - 6 == 0)
758
+
759
+ sage: points = [(1,0,0), (0,1,0), (0,0,1),
760
+ ....: (-1,0,0), (0,-1,0), (0,0,-1)]
761
+ sage: L = LatticePolytope(points)
762
+ sage: C = CombinatorialPolyhedron(L)
763
+ sage: C.Hrepresentation()
764
+ (N(1, -1, -1),
765
+ N(1, 1, -1),
766
+ N(1, 1, 1),
767
+ N(1, -1, 1),
768
+ N(-1, -1, 1),
769
+ N(-1, -1, -1),
770
+ N(-1, 1, -1),
771
+ N(-1, 1, 1))
772
+
773
+ sage: M = Cone([(1,0), (0,1)])
774
+ sage: CombinatorialPolyhedron(M).Hrepresentation()
775
+ (M(0, 1), M(1, 0))
776
+ """
777
+ if self.facet_names() is not None:
778
+ return self.facet_names() + self.equations()
779
+ else:
780
+ return tuple(smallInteger(i) for i in range(self.n_Hrepresentation()))
781
+
782
+ def dimension(self):
783
+ r"""
784
+ Return the dimension of the polyhedron.
785
+
786
+ EXAMPLES::
787
+
788
+ sage: C = CombinatorialPolyhedron([(1,2,3), (1,2,4),
789
+ ....: (1,3,4), (2,3,4)])
790
+ sage: C.dimension()
791
+ 3
792
+
793
+ sage: P = Polyhedron(rays=[[1,0,0],[0,1,0],[0,0,1],[0,0,-1]])
794
+ sage: CombinatorialPolyhedron(P).dimension()
795
+ 3
796
+
797
+ ``dim`` is an alias::
798
+
799
+ sage: CombinatorialPolyhedron(P).dim()
800
+ 3
801
+ """
802
+ if self._dimension == -2:
803
+ # Dimension not computed yet.
804
+ if self.n_facets() == -1:
805
+ raise ValueError("the combinatorial polyhedron was not initialized")
806
+ elif self.n_facets() == 0:
807
+ # The dimension of a trivial polyhedron is assumed to contain
808
+ # exactly one "vertex" and for each dimension one "line" as in
809
+ # :class:`~sage.geometry.polyhedron.parent.Polyhedron_base`
810
+ self._dimension = self.n_Vrepresentation() - 1
811
+ elif not self.is_bounded() or self.n_facets() <= self.n_Vrepresentation():
812
+ self._dimension = self.bitrep_facets().compute_dimension()
813
+ else:
814
+ # If the polyhedron has many facets,
815
+ # calculating the dimension of the dual will be faster.
816
+ # The dual exists, if the polyhedron is bounded.
817
+ self._dimension = self.bitrep_facets().compute_dimension()
818
+ return smallInteger(self._dimension)
819
+
820
+ dim = dimension
821
+
822
+ @cached_method
823
+ def n_vertices(self):
824
+ r"""
825
+ Return the number of vertices.
826
+
827
+ Is equivalent to ``len(self.vertices())``.
828
+
829
+ EXAMPLES::
830
+
831
+ sage: P = polytopes.cube()
832
+ sage: C = CombinatorialPolyhedron(P)
833
+ sage: C.n_vertices()
834
+ 8
835
+
836
+ sage: P = polytopes.cyclic_polytope(4,20)
837
+ sage: C = CombinatorialPolyhedron(P)
838
+ sage: C.n_vertices()
839
+ 20
840
+
841
+ sage: P = Polyhedron(lines=[[0,1]], vertices=[[1,0], [-1,0]])
842
+ sage: C = CombinatorialPolyhedron(P)
843
+ sage: C.n_vertices()
844
+ 0
845
+
846
+ sage: P = Polyhedron(rays=[[1,0,0], [0,1,0]], lines=[[0,0,1]])
847
+ sage: C = CombinatorialPolyhedron(P)
848
+ sage: C.n_vertices()
849
+ 0
850
+
851
+ sage: C = CombinatorialPolyhedron(4)
852
+ sage: C.f_vector()
853
+ (1, 0, 0, 0, 0, 1)
854
+ sage: C.n_vertices()
855
+ 0
856
+
857
+ sage: C = CombinatorialPolyhedron(0)
858
+ sage: C.f_vector()
859
+ (1, 1)
860
+ sage: C.n_vertices()
861
+ 1
862
+ """
863
+ if self.dimension() == 0:
864
+ # This specific trivial polyhedron needs special attention.
865
+ return smallInteger(1)
866
+ if not self.is_bounded():
867
+ # Some elements in the ``Vrep`` might not correspond to actual combinatorial vertices.
868
+ return len(self.vertices())
869
+ else:
870
+ return smallInteger(self.n_Vrepresentation())
871
+
872
+ def vertices(self, names=True):
873
+ r"""
874
+ Return the elements in the Vrepresentation that are vertices.
875
+
876
+ In case of an unbounded polyhedron, there might be lines and
877
+ rays in the Vrepresentation.
878
+
879
+ If ``names`` is set to ``False``, then the vertices are given by
880
+ their indices in the Vrepresentation.
881
+
882
+ EXAMPLES::
883
+
884
+ sage: P = Polyhedron(rays=[[1,0,0],[0,1,0],[0,0,1]])
885
+ sage: C = CombinatorialPolyhedron(P)
886
+ sage: C.vertices()
887
+ (A vertex at (0, 0, 0),)
888
+ sage: C.Vrepresentation()
889
+ (A vertex at (0, 0, 0),
890
+ A ray in the direction (0, 0, 1),
891
+ A ray in the direction (0, 1, 0),
892
+ A ray in the direction (1, 0, 0))
893
+ sage: P = polytopes.cross_polytope(3)
894
+ sage: C = CombinatorialPolyhedron(P)
895
+ sage: C.vertices()
896
+ (A vertex at (-1, 0, 0),
897
+ A vertex at (0, -1, 0),
898
+ A vertex at (0, 0, -1),
899
+ A vertex at (0, 0, 1),
900
+ A vertex at (0, 1, 0),
901
+ A vertex at (1, 0, 0))
902
+ sage: C.vertices(names=False)
903
+ (0, 1, 2, 3, 4, 5)
904
+
905
+ sage: points = [(1,0,0), (0,1,0), (0,0,1),
906
+ ....: (-1,0,0), (0,-1,0), (0,0,-1)]
907
+ sage: L = LatticePolytope(points)
908
+ sage: C = CombinatorialPolyhedron(L)
909
+ sage: C.vertices()
910
+ (M(1, 0, 0), M(0, 1, 0), M(0, 0, 1), M(-1, 0, 0), M(0, -1, 0), M(0, 0, -1))
911
+ sage: C.vertices(names=False)
912
+ (0, 1, 2, 3, 4, 5)
913
+
914
+ sage: P = Polyhedron(vertices=[[0,0]])
915
+ sage: C = CombinatorialPolyhedron(P)
916
+ sage: C.vertices()
917
+ (A vertex at (0, 0),)
918
+ """
919
+ if unlikely(self.dimension() == 0):
920
+ # Handling the case of a trivial polyhedron of dimension `0`.
921
+ if names and self.Vrep():
922
+ return (self.Vrep()[0],)
923
+ else:
924
+ return (smallInteger(0),)
925
+ if not self.is_bounded():
926
+ it = self.face_iter(0)
927
+ try:
928
+ # The Polyhedron has at least one vertex.
929
+ # In this case every element in the ``Vrep``
930
+ # that is not contained in the far face
931
+ # is a vertex.
932
+ next(it)
933
+ except StopIteration:
934
+ # The Polyhedron has no vertex.
935
+ return ()
936
+ if names and self.Vrep():
937
+ return tuple(self.Vrep()[i] for i in range(self.n_Vrepresentation()) if i not in self.far_face_tuple())
938
+ else:
939
+ return tuple(smallInteger(i) for i in range(self.n_Vrepresentation()) if i not in self.far_face_tuple())
940
+
941
+ def n_facets(self):
942
+ r"""
943
+ Return the number of facets.
944
+
945
+ Is equivalent to ``len(self.facets())``.
946
+
947
+ EXAMPLES::
948
+
949
+ sage: P = polytopes.cube()
950
+ sage: C = CombinatorialPolyhedron(P)
951
+ sage: C.n_facets()
952
+ 6
953
+
954
+ sage: P = polytopes.cyclic_polytope(4,20)
955
+ sage: C = CombinatorialPolyhedron(P)
956
+ sage: C.n_facets()
957
+ 170
958
+
959
+ sage: P = Polyhedron(lines=[[0,1]], vertices=[[1,0], [-1,0]])
960
+ sage: C = CombinatorialPolyhedron(P)
961
+ sage: C.n_facets()
962
+ 2
963
+
964
+ sage: P = Polyhedron(rays=[[1,0], [-1,0], [0,1]])
965
+ sage: C = CombinatorialPolyhedron(P)
966
+ sage: C.n_facets()
967
+ 1
968
+
969
+ sage: C = CombinatorialPolyhedron(-1)
970
+ sage: C.f_vector()
971
+ (1)
972
+ sage: C.n_facets()
973
+ 0
974
+
975
+ Facets are defined to be the maximal nontrivial faces.
976
+ The ``0``-dimensional polyhedron does not have nontrivial faces::
977
+
978
+ sage: C = CombinatorialPolyhedron(0)
979
+ sage: C.f_vector()
980
+ (1, 1)
981
+ sage: C.n_facets()
982
+ 0
983
+ """
984
+ if unlikely(self._dimension == 0):
985
+ # This trivial polyhedron needs special attention.
986
+ return smallInteger(0)
987
+ return smallInteger(self._n_facets)
988
+
989
+ def facets(self, names=True):
990
+ r"""
991
+ Return the facets as lists of ``[vertices, rays, lines]``.
992
+
993
+ If ``names`` is ``False``, then the Vrepresentatives in the facets
994
+ are given by their indices in the Vrepresentation.
995
+
996
+ The facets are the maximal nontrivial faces.
997
+
998
+ EXAMPLES::
999
+
1000
+ sage: P = polytopes.cube()
1001
+ sage: C = CombinatorialPolyhedron(P)
1002
+ sage: C.facets()
1003
+ ((A vertex at (1, -1, -1),
1004
+ A vertex at (1, 1, -1),
1005
+ A vertex at (1, 1, 1),
1006
+ A vertex at (1, -1, 1)),
1007
+ (A vertex at (1, 1, -1),
1008
+ A vertex at (1, 1, 1),
1009
+ A vertex at (-1, 1, -1),
1010
+ A vertex at (-1, 1, 1)),
1011
+ (A vertex at (1, 1, 1),
1012
+ A vertex at (1, -1, 1),
1013
+ A vertex at (-1, -1, 1),
1014
+ A vertex at (-1, 1, 1)),
1015
+ (A vertex at (-1, -1, 1),
1016
+ A vertex at (-1, -1, -1),
1017
+ A vertex at (-1, 1, -1),
1018
+ A vertex at (-1, 1, 1)),
1019
+ (A vertex at (1, -1, -1),
1020
+ A vertex at (1, 1, -1),
1021
+ A vertex at (-1, -1, -1),
1022
+ A vertex at (-1, 1, -1)),
1023
+ (A vertex at (1, -1, -1),
1024
+ A vertex at (1, -1, 1),
1025
+ A vertex at (-1, -1, 1),
1026
+ A vertex at (-1, -1, -1)))
1027
+ sage: C.facets(names=False)
1028
+ ((0, 1, 2, 3),
1029
+ (1, 2, 6, 7),
1030
+ (2, 3, 4, 7),
1031
+ (4, 5, 6, 7),
1032
+ (0, 1, 5, 6),
1033
+ (0, 3, 4, 5))
1034
+
1035
+ The empty face is trivial and hence the ``0``-dimensional
1036
+ polyhedron does not have facets::
1037
+
1038
+ sage: C = CombinatorialPolyhedron(0)
1039
+ sage: C.facets()
1040
+ ()
1041
+ """
1042
+ if unlikely(self.dimension() <= 0):
1043
+ # Special attention for this trivial case.
1044
+ # Facets are defined to be nontrivial faces of codimension 1.
1045
+ # The empty face is trivial.
1046
+ return ()
1047
+
1048
+ # It is essential to have the facets in the exact same order as
1049
+ # on input, so that pickle/unpickle by :meth:`reduce` works.
1050
+ # Every facet knows its index by the facet representation.
1051
+ face_iter = self.face_iter(self.dimension() - 1, algorithm='primal')
1052
+ facets = [None] * self.n_facets()
1053
+ for face in face_iter:
1054
+ index = face.ambient_H_indices()[0]
1055
+ if names:
1056
+ verts = face.ambient_Vrepresentation()
1057
+ else:
1058
+ verts = face.ambient_V_indices()
1059
+ facets[index] = verts
1060
+
1061
+ return tuple(facets)
1062
+
1063
+ @cached_method
1064
+ def incidence_matrix(self):
1065
+ """
1066
+ Return the incidence matrix.
1067
+
1068
+ .. NOTE::
1069
+
1070
+ The columns correspond to inequalities/equations in the
1071
+ order :meth:`Hrepresentation`, the rows correspond to
1072
+ vertices/rays/lines in the order
1073
+ :meth:`Vrepresentation`.
1074
+
1075
+ .. SEEALSO::
1076
+
1077
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix`.
1078
+
1079
+ EXAMPLES::
1080
+
1081
+ sage: P = polytopes.cube()
1082
+ sage: C = P.combinatorial_polyhedron()
1083
+ sage: C.incidence_matrix()
1084
+ [1 0 0 0 1 1]
1085
+ [1 1 0 0 1 0]
1086
+ [1 1 1 0 0 0]
1087
+ [1 0 1 0 0 1]
1088
+ [0 0 1 1 0 1]
1089
+ [0 0 0 1 1 1]
1090
+ [0 1 0 1 1 0]
1091
+ [0 1 1 1 0 0]
1092
+
1093
+ In this case the incidence matrix is only computed once::
1094
+
1095
+ sage: P.incidence_matrix() is C.incidence_matrix()
1096
+ True
1097
+ sage: C.incidence_matrix.clear_cache()
1098
+ sage: C.incidence_matrix() is P.incidence_matrix()
1099
+ False
1100
+ sage: C.incidence_matrix() == P.incidence_matrix()
1101
+ True
1102
+
1103
+ ::
1104
+
1105
+ sage: # needs sage.combinat
1106
+ sage: P = polytopes.permutahedron(5, backend='field')
1107
+ sage: C = P.combinatorial_polyhedron()
1108
+ sage: C.incidence_matrix.clear_cache()
1109
+ sage: C.incidence_matrix() == P.incidence_matrix()
1110
+ True
1111
+
1112
+ The incidence matrix is consistent with
1113
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix`::
1114
+
1115
+ sage: P = Polyhedron([[0,0]])
1116
+ sage: P.incidence_matrix()
1117
+ [1 1]
1118
+ sage: C = P.combinatorial_polyhedron()
1119
+ sage: C.incidence_matrix.clear_cache()
1120
+ sage: P.combinatorial_polyhedron().incidence_matrix()
1121
+ [1 1]
1122
+
1123
+ TESTS:
1124
+
1125
+ Check that :issue:`29455` is fixed::
1126
+
1127
+ sage: C = Polyhedron([[0]]).combinatorial_polyhedron()
1128
+ sage: C.incidence_matrix.clear_cache()
1129
+ sage: C.incidence_matrix()
1130
+ [1]
1131
+ sage: C = CombinatorialPolyhedron(-1)
1132
+ sage: C.incidence_matrix.clear_cache()
1133
+ sage: C.incidence_matrix()
1134
+ []
1135
+
1136
+ Check that the base ring is ``ZZ``, see :issue:`29840`::
1137
+
1138
+ sage: C = CombinatorialPolyhedron([[0,1,2], [0,1,3], [0,2,3], [1,2,3]])
1139
+ sage: C.incidence_matrix().base_ring()
1140
+ Integer Ring
1141
+ """
1142
+ from sage.rings.integer_ring import ZZ
1143
+ from sage.matrix.constructor import matrix
1144
+ incidence_matrix = matrix(
1145
+ ZZ, self.n_Vrepresentation(), self.n_Hrepresentation(), 0)
1146
+
1147
+ if self.dim() < 1:
1148
+ # Small cases.
1149
+ if self.dim() == 0:
1150
+ try:
1151
+ # To be consistent with ``Polyhedron_base``,
1152
+ for i in range(self.n_Hrepresentation()):
1153
+ incidence_matrix.set_unsafe_int(0, i, 1)
1154
+ except AttributeError:
1155
+ for i in range(self.n_Hrepresentation()):
1156
+ incidence_matrix[0, i] = 1
1157
+ incidence_matrix.set_immutable()
1158
+ return incidence_matrix
1159
+
1160
+ # If equations are present, we add them as last columns.
1161
+ n_facets = self.n_facets()
1162
+ if self.facet_names() is not None:
1163
+ n_equations = len(self.equations())
1164
+ try:
1165
+ for Hindex in range(n_facets, n_facets + n_equations):
1166
+ for Vindex in range(self.n_Vrepresentation()):
1167
+ incidence_matrix.set_unsafe_int(Vindex, Hindex, 1)
1168
+ except AttributeError:
1169
+ for Hindex in range(n_facets, n_facets + n_equations):
1170
+ for Vindex in range(self.n_Vrepresentation()):
1171
+ incidence_matrix[Vindex, Hindex] = 1
1172
+
1173
+ facet_iter = self.face_iter(self.dimension() - 1, algorithm='primal')
1174
+ for facet in facet_iter:
1175
+ Hindex = facet.ambient_H_indices()[0]
1176
+ try:
1177
+ for Vindex in facet.ambient_V_indices():
1178
+ incidence_matrix.set_unsafe_int(Vindex, Hindex, 1)
1179
+ except AttributeError:
1180
+ for Vindex in facet.ambient_V_indices():
1181
+ incidence_matrix[Vindex, Hindex] = 1
1182
+
1183
+ incidence_matrix.set_immutable()
1184
+
1185
+ return incidence_matrix
1186
+
1187
+ cdef int _algorithm_to_dual(self, algorithm) except -2:
1188
+ if algorithm == 'primal':
1189
+ return 0
1190
+ elif algorithm == 'dual':
1191
+ if not self.is_bounded():
1192
+ raise ValueError("dual algorithm only available for bounded polyhedra")
1193
+ return 1
1194
+ elif algorithm is None:
1195
+ return -1
1196
+ else:
1197
+ raise ValueError("algorithm must be 'primal', 'dual' or None")
1198
+
1199
+ def edges(self, names=True, algorithm=None):
1200
+ r"""
1201
+ Return the edges of the polyhedron, i.e. the rank 1 faces.
1202
+
1203
+ INPUT:
1204
+
1205
+ - ``names`` -- boolean (default: ``True``); if ``False``,
1206
+ then the Vrepresentatives in the edges are given by
1207
+ their indices in the Vrepresentation
1208
+
1209
+ - ``algorithm`` -- string (optional);
1210
+ specify whether the face generator starts with facets or vertices:
1211
+ * ``'primal'`` -- start with the facets
1212
+ * ``'dual'`` -- start with the vertices
1213
+ * ``None`` -- choose automatically
1214
+
1215
+ .. NOTE::
1216
+
1217
+ To compute edges and f_vector, first compute the edges.
1218
+ This might be faster.
1219
+
1220
+ EXAMPLES::
1221
+
1222
+ sage: P = polytopes.cyclic_polytope(3,5)
1223
+ sage: C = CombinatorialPolyhedron(P)
1224
+ sage: C.edges()
1225
+ ((A vertex at (3, 9, 27), A vertex at (4, 16, 64)),
1226
+ (A vertex at (2, 4, 8), A vertex at (4, 16, 64)),
1227
+ (A vertex at (1, 1, 1), A vertex at (4, 16, 64)),
1228
+ (A vertex at (0, 0, 0), A vertex at (4, 16, 64)),
1229
+ (A vertex at (2, 4, 8), A vertex at (3, 9, 27)),
1230
+ (A vertex at (0, 0, 0), A vertex at (3, 9, 27)),
1231
+ (A vertex at (1, 1, 1), A vertex at (2, 4, 8)),
1232
+ (A vertex at (0, 0, 0), A vertex at (2, 4, 8)),
1233
+ (A vertex at (0, 0, 0), A vertex at (1, 1, 1)))
1234
+
1235
+ sage: C.edges(names=False)
1236
+ ((3, 4), (2, 4), (1, 4), (0, 4), (2, 3), (0, 3), (1, 2), (0, 2), (0, 1))
1237
+
1238
+ sage: P = Polyhedron(rays=[[-1,0],[1,0]])
1239
+ sage: C = CombinatorialPolyhedron(P)
1240
+ sage: C.edges()
1241
+ ((A line in the direction (1, 0), A vertex at (0, 0)),)
1242
+
1243
+ sage: P = Polyhedron(vertices=[[0,0],[1,0]])
1244
+ sage: C = CombinatorialPolyhedron(P)
1245
+ sage: C.edges()
1246
+ ((A vertex at (0, 0), A vertex at (1, 0)),)
1247
+
1248
+ sage: from itertools import combinations
1249
+ sage: N = combinations(['a','b','c','d','e'], 4)
1250
+ sage: C = CombinatorialPolyhedron(N)
1251
+ sage: C.edges()
1252
+ (('d', 'e'),
1253
+ ('c', 'e'),
1254
+ ('b', 'e'),
1255
+ ('a', 'e'),
1256
+ ('c', 'd'),
1257
+ ('b', 'd'),
1258
+ ('a', 'd'),
1259
+ ('b', 'c'),
1260
+ ('a', 'c'),
1261
+ ('a', 'b'))
1262
+ """
1263
+ self._compute_edges(self._algorithm_to_dual(algorithm))
1264
+
1265
+ # Mapping the indices of the Vrep to the names, if requested.
1266
+ if self.Vrep() is not None and names is True:
1267
+ def f(size_t i):
1268
+ return self.Vrep()[i]
1269
+ else:
1270
+ def f(size_t i):
1271
+ return smallInteger(i)
1272
+
1273
+ cdef size_t j
1274
+ return tuple((f(self._edges.get(j).first),
1275
+ f(self._edges.get(j).second))
1276
+ for j in range(self._edges.length))
1277
+
1278
+ def vertex_graph(self, names=True, algorithm=None):
1279
+ r"""
1280
+ Return a graph in which the vertices correspond to vertices
1281
+ of the polyhedron, and edges to bounded rank 1 faces.
1282
+
1283
+ INPUT:
1284
+
1285
+ - ``names`` -- boolean (default: ``True``); if ``False``,
1286
+ then the nodes of the graph are labeld by the
1287
+ indices of the Vrepresentation
1288
+
1289
+ - ``algorithm`` -- string (optional);
1290
+ specify whether the face generator starts with facets or vertices:
1291
+ * ``'primal'`` -- start with the facets
1292
+ * ``'dual'`` -- start with the vertices
1293
+ * ``None`` -- choose automatically
1294
+
1295
+ EXAMPLES::
1296
+
1297
+ sage: P = polytopes.cyclic_polytope(3,5)
1298
+ sage: C = CombinatorialPolyhedron(P)
1299
+ sage: G = C.vertex_graph(); G # needs sage.graphs
1300
+ Graph on 5 vertices
1301
+ sage: sorted(G.degree()) # needs sage.graphs
1302
+ [3, 3, 4, 4, 4]
1303
+
1304
+ sage: P = Polyhedron(rays=[[1]])
1305
+ sage: C = CombinatorialPolyhedron(P)
1306
+ sage: C.graph() # needs sage.graphs
1307
+ Graph on 1 vertex
1308
+ """
1309
+ vertices = self.vertices(names=names)
1310
+
1311
+ # Getting the bounded edges.
1312
+ edges = tuple(edge for edge in self.edges(names=names, algorithm=algorithm)
1313
+ if edge[0] in vertices and edge[1] in vertices)
1314
+
1315
+ from sage.graphs.graph import Graph
1316
+ return Graph([vertices, edges], format='vertices_and_edges')
1317
+
1318
+ graph = vertex_graph
1319
+
1320
+ @cached_method
1321
+ def vertex_adjacency_matrix(self, algorithm=None):
1322
+ """
1323
+ Return the binary matrix of vertex adjacencies.
1324
+
1325
+ INPUT:
1326
+
1327
+ - ``algorithm`` -- string (optional);
1328
+ specify whether the face generator starts with facets or vertices:
1329
+ * ``'primal'`` -- start with the facets
1330
+ * ``'dual'`` -- start with the vertices
1331
+ * ``None`` -- choose automatically
1332
+
1333
+ .. SEEALSO::
1334
+
1335
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.vertex_adjacency_matrix`.
1336
+
1337
+ EXAMPLES::
1338
+
1339
+ sage: P = polytopes.cube()
1340
+ sage: C = P.combinatorial_polyhedron()
1341
+ sage: C.vertex_adjacency_matrix()
1342
+ [0 1 0 1 0 1 0 0]
1343
+ [1 0 1 0 0 0 1 0]
1344
+ [0 1 0 1 0 0 0 1]
1345
+ [1 0 1 0 1 0 0 0]
1346
+ [0 0 0 1 0 1 0 1]
1347
+ [1 0 0 0 1 0 1 0]
1348
+ [0 1 0 0 0 1 0 1]
1349
+ [0 0 1 0 1 0 1 0]
1350
+
1351
+ TESTS::
1352
+
1353
+ sage: CombinatorialPolyhedron(-1).vertex_adjacency_matrix()
1354
+ []
1355
+ sage: CombinatorialPolyhedron(0).vertex_adjacency_matrix()
1356
+ [0]
1357
+ sage: polytopes.cube().vertex_adjacency_matrix().is_immutable()
1358
+ True
1359
+ """
1360
+ from sage.rings.integer_ring import ZZ
1361
+ from sage.matrix.constructor import matrix
1362
+ cdef Matrix_dense adjacency_matrix = matrix(
1363
+ ZZ, self.n_Vrepresentation(), self.n_Vrepresentation(), 0)
1364
+ cdef size_t i, first, second
1365
+
1366
+ self._compute_edges(self._algorithm_to_dual(algorithm))
1367
+ try:
1368
+ for i in range(self._edges.length):
1369
+ first = self._edges.get(i).first
1370
+ second = self._edges.get(i).second
1371
+ adjacency_matrix.set_unsafe_int(first, second, 1)
1372
+ adjacency_matrix.set_unsafe_int(second, first, 1)
1373
+ except AttributeError:
1374
+ first = self._edges.get(i).first
1375
+ second = self._edges.get(i).second
1376
+ adjacency_matrix[first, second] = adjacency_matrix[second, first] = 1
1377
+ adjacency_matrix.set_immutable()
1378
+ return adjacency_matrix
1379
+
1380
+ def ridges(self, add_equations=False, names=True, algorithm=None):
1381
+ r"""
1382
+ Return the ridges.
1383
+
1384
+ The ridges of a polyhedron are the faces
1385
+ contained in exactly two facets.
1386
+
1387
+ To obtain all faces of codimension 1 use
1388
+ :meth:`CombinatorialPolyhedron.face_generator` instead.
1389
+
1390
+ The ridges will be given by the facets, they are contained in.
1391
+
1392
+ INPUT:
1393
+
1394
+ - ``add_equations`` -- if ``True``, then equations of the polyhedron
1395
+ will be added (only applicable when ``names`` is ``True``)
1396
+
1397
+ - ``names`` -- boolean (default: ``True``);
1398
+ if ``False``, then the facets are given by their indices
1399
+
1400
+ - ``algorithm`` -- string (optional);
1401
+ specify whether the face generator starts with facets or vertices:
1402
+ * ``'primal'`` -- start with the facets
1403
+ * ``'dual'`` -- start with the vertices
1404
+ * ``None`` -- choose automatically
1405
+
1406
+ .. NOTE::
1407
+
1408
+ To compute ridges and f_vector, compute the ridges first.
1409
+ This might be faster.
1410
+
1411
+ EXAMPLES::
1412
+
1413
+ sage: # needs sage.combinat
1414
+ sage: P = polytopes.permutahedron(2)
1415
+ sage: C = CombinatorialPolyhedron(P)
1416
+ sage: C.ridges()
1417
+ ((An inequality (1, 0) x - 1 >= 0, An inequality (-1, 0) x + 2 >= 0),)
1418
+ sage: C.ridges(add_equations=True)
1419
+ (((An inequality (1, 0) x - 1 >= 0, An equation (1, 1) x - 3 == 0),
1420
+ (An inequality (-1, 0) x + 2 >= 0, An equation (1, 1) x - 3 == 0)),)
1421
+
1422
+ sage: P = polytopes.cyclic_polytope(4,5)
1423
+ sage: C = CombinatorialPolyhedron(P)
1424
+ sage: C.ridges()
1425
+ ((An inequality (24, -26, 9, -1) x + 0 >= 0,
1426
+ An inequality (-50, 35, -10, 1) x + 24 >= 0),
1427
+ (An inequality (-12, 19, -8, 1) x + 0 >= 0,
1428
+ An inequality (-50, 35, -10, 1) x + 24 >= 0),
1429
+ (An inequality (8, -14, 7, -1) x + 0 >= 0,
1430
+ An inequality (-50, 35, -10, 1) x + 24 >= 0),
1431
+ (An inequality (-6, 11, -6, 1) x + 0 >= 0,
1432
+ An inequality (-50, 35, -10, 1) x + 24 >= 0),
1433
+ (An inequality (-12, 19, -8, 1) x + 0 >= 0,
1434
+ An inequality (24, -26, 9, -1) x + 0 >= 0),
1435
+ (An inequality (8, -14, 7, -1) x + 0 >= 0,
1436
+ An inequality (24, -26, 9, -1) x + 0 >= 0),
1437
+ (An inequality (-6, 11, -6, 1) x + 0 >= 0,
1438
+ An inequality (24, -26, 9, -1) x + 0 >= 0),
1439
+ (An inequality (8, -14, 7, -1) x + 0 >= 0,
1440
+ An inequality (-12, 19, -8, 1) x + 0 >= 0),
1441
+ (An inequality (-6, 11, -6, 1) x + 0 >= 0,
1442
+ An inequality (-12, 19, -8, 1) x + 0 >= 0),
1443
+ (An inequality (-6, 11, -6, 1) x + 0 >= 0,
1444
+ An inequality (8, -14, 7, -1) x + 0 >= 0))
1445
+ sage: C.ridges(names=False)
1446
+ ((3, 4),
1447
+ (2, 4),
1448
+ (1, 4),
1449
+ (0, 4),
1450
+ (2, 3),
1451
+ (1, 3),
1452
+ (0, 3),
1453
+ (1, 2),
1454
+ (0, 2),
1455
+ (0, 1))
1456
+
1457
+ sage: P = Polyhedron(rays=[[1,0]])
1458
+ sage: C = CombinatorialPolyhedron(P)
1459
+ sage: C
1460
+ A 1-dimensional combinatorial polyhedron with 1 facet
1461
+ sage: C.ridges()
1462
+ ()
1463
+ sage: it = C.face_generator(0)
1464
+ sage: for face in it: face.ambient_Hrepresentation()
1465
+ (An inequality (1, 0) x + 0 >= 0, An equation (0, 1) x + 0 == 0)
1466
+
1467
+ TESTS:
1468
+
1469
+ Testing that ``add_equations`` is ignored if ``names`` is ``False``::
1470
+
1471
+ sage: C = CombinatorialPolyhedron(polytopes.simplex())
1472
+ sage: C.ridges(names=False, add_equations=True)
1473
+ ((2, 3), (1, 3), (0, 3), (1, 2), (0, 2), (0, 1))
1474
+ """
1475
+ self._compute_ridges(self._algorithm_to_dual(algorithm))
1476
+ cdef size_t n_ridges = self._ridges.length
1477
+
1478
+ # Mapping the indices of the Vepr to the names, if requested.
1479
+ if self.facet_names() is not None and names is True:
1480
+ def f(size_t i):
1481
+ return self.facet_names()[i]
1482
+ else:
1483
+ def f(size_t i):
1484
+ return smallInteger(i)
1485
+
1486
+ if add_equations and names:
1487
+ return tuple(
1488
+ ((f(self._ridges.get(i).first),) + self.equations(),
1489
+ (f(self._ridges.get(i).second),) + self.equations())
1490
+ for i in range(n_ridges))
1491
+
1492
+ return tuple(
1493
+ (f(self._ridges.get(i).first),
1494
+ f(self._ridges.get(i).second))
1495
+ for i in range(n_ridges))
1496
+
1497
+ @cached_method
1498
+ def facet_adjacency_matrix(self, algorithm=None):
1499
+ """
1500
+ Return the binary matrix of facet adjacencies.
1501
+
1502
+ INPUT:
1503
+
1504
+ - ``algorithm`` -- string (optional);
1505
+ specify whether the face generator starts with facets or vertices:
1506
+ * ``'primal'`` -- start with the facets
1507
+ * ``'dual'`` -- start with the vertices
1508
+ * ``None`` -- choose automatically
1509
+
1510
+ .. SEEALSO::
1511
+
1512
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.vertex_adjacency_matrix`.
1513
+
1514
+ EXAMPLES::
1515
+
1516
+ sage: P = polytopes.cube()
1517
+ sage: C = P.combinatorial_polyhedron()
1518
+ sage: C.facet_adjacency_matrix()
1519
+ [0 1 1 0 1 1]
1520
+ [1 0 1 1 1 0]
1521
+ [1 1 0 1 0 1]
1522
+ [0 1 1 0 1 1]
1523
+ [1 1 0 1 0 1]
1524
+ [1 0 1 1 1 0]
1525
+
1526
+ TESTS::
1527
+
1528
+ sage: CombinatorialPolyhedron(-1).facet_adjacency_matrix()
1529
+ []
1530
+ sage: CombinatorialPolyhedron(0).facet_adjacency_matrix()
1531
+ []
1532
+ sage: polytopes.cube().facet_adjacency_matrix().is_immutable()
1533
+ True
1534
+ """
1535
+ from sage.rings.integer_ring import ZZ
1536
+ from sage.matrix.constructor import matrix
1537
+ cdef Matrix_dense adjacency_matrix = matrix(
1538
+ ZZ, self.n_facets(), self.n_facets(), 0)
1539
+ cdef size_t i
1540
+
1541
+ self._compute_ridges(self._algorithm_to_dual(algorithm))
1542
+ try:
1543
+ for i in range(self._ridges.length):
1544
+ first = self._ridges.get(i).first
1545
+ second = self._ridges.get(i).second
1546
+ adjacency_matrix.set_unsafe_int(first, second, 1)
1547
+ adjacency_matrix.set_unsafe_int(second, first, 1)
1548
+ except AttributeError:
1549
+ for i in range(self._ridges.length):
1550
+ first = self._ridges.get(i).first
1551
+ second = self._ridges.get(i).second
1552
+ adjacency_matrix[first, second] = adjacency_matrix[second, first] = 1
1553
+ adjacency_matrix.set_immutable()
1554
+ return adjacency_matrix
1555
+
1556
+ def facet_graph(self, names=True, algorithm=None):
1557
+ r"""
1558
+ Return the facet graph.
1559
+
1560
+ The facet graph of a polyhedron consists of
1561
+ ridges as edges and facets as vertices.
1562
+
1563
+ INPUT:
1564
+
1565
+ - ``algorithm`` -- string (optional);
1566
+ specify whether the face generator starts with facets or vertices:
1567
+
1568
+ * ``'primal'`` -- start with the facets
1569
+ * ``'dual'`` -- start with the vertices
1570
+ * ``None`` -- choose automatically
1571
+
1572
+ If ``names`` is ``False``, the ``vertices`` of the graph will
1573
+ be the indices of the facets in the Hrepresentation.
1574
+
1575
+ EXAMPLES::
1576
+
1577
+ sage: P = polytopes.cyclic_polytope(4,6)
1578
+ sage: C = CombinatorialPolyhedron(P)
1579
+ sage: C.facet_graph() # needs sage.graphs
1580
+ Graph on 9 vertices
1581
+
1582
+ TESTS::
1583
+
1584
+ sage: P = Polyhedron(ieqs=[[1,-1,0],[1,1,0]])
1585
+ sage: CombinatorialPolyhedron(P).facet_graph() # needs sage.graphs
1586
+ Graph on 2 vertices
1587
+
1588
+ Checking that :issue:`28604` is fixed::
1589
+
1590
+ sage: C = CombinatorialPolyhedron(polytopes.cube()); C
1591
+ A 3-dimensional combinatorial polyhedron with 6 facets
1592
+ sage: C.facet_graph(names=False) # needs sage.graphs
1593
+ Graph on 6 vertices
1594
+
1595
+ sage: C = CombinatorialPolyhedron(polytopes.hypersimplex(5,2)); C
1596
+ A 4-dimensional combinatorial polyhedron with 10 facets
1597
+ sage: C.facet_graph() # needs sage.combinat sage.graphs
1598
+ Graph on 10 vertices
1599
+ """
1600
+ face_iter = self.face_iter(self.dimension() - 1, algorithm='primal')
1601
+ if names:
1602
+ V = list(facet.ambient_Hrepresentation() for facet in face_iter)
1603
+ else:
1604
+ V = list(facet.ambient_V_indices() for facet in face_iter)
1605
+ E = self.ridges(names=names, add_equations=True, algorithm=algorithm)
1606
+ if not names:
1607
+ # If names is false, the ridges are given as tuple of indices,
1608
+ # i.e. (1,2) instead of (('f1',), ('f2',)).
1609
+ V = list(v[0] for v in V)
1610
+ from sage.graphs.graph import Graph
1611
+ return Graph([V, E], format='vertices_and_edges')
1612
+
1613
+ @cached_method
1614
+ def vertex_facet_graph(self, names=True):
1615
+ r"""
1616
+ Return the vertex-facet graph.
1617
+
1618
+ This method constructs a directed bipartite graph.
1619
+ The nodes of the graph correspond to elements of the Vrepresentation
1620
+ and facets. There is a directed edge from Vrepresentation to facets
1621
+ for each incidence.
1622
+
1623
+ If ``names`` is set to ``False``, then the vertices (of the graph) are given by
1624
+ integers.
1625
+
1626
+ INPUT:
1627
+
1628
+ - ``names`` -- boolean (default: ``True``); if ``True`` label the vertices of the
1629
+ graph by the corresponding names of the Vrepresentation resp. Hrepresentation;
1630
+ if ``False`` label the vertices of the graph by integers
1631
+
1632
+ EXAMPLES::
1633
+
1634
+ sage: P = polytopes.hypercube(2).pyramid()
1635
+ sage: C = CombinatorialPolyhedron(P)
1636
+ sage: G = C.vertex_facet_graph(); G # needs sage.graphs
1637
+ Digraph on 10 vertices
1638
+ sage: C.Vrepresentation()
1639
+ (A vertex at (0, -1, -1),
1640
+ A vertex at (0, -1, 1),
1641
+ A vertex at (0, 1, -1),
1642
+ A vertex at (0, 1, 1),
1643
+ A vertex at (1, 0, 0))
1644
+ sage: sorted(G.neighbors_out(C.Vrepresentation()[4])) # needs sage.graphs
1645
+ [An inequality (-1, -1, 0) x + 1 >= 0,
1646
+ An inequality (-1, 0, -1) x + 1 >= 0,
1647
+ An inequality (-1, 0, 1) x + 1 >= 0,
1648
+ An inequality (-1, 1, 0) x + 1 >= 0]
1649
+
1650
+ If ``names`` is ``True`` (the default) but the combinatorial polyhedron
1651
+ has been initialized without specifying names to
1652
+ ``Vrepresentation`` and ``Hrepresentation``,
1653
+ then indices of the Vrepresentation and the facets will be used along
1654
+ with a string 'H' or 'V'::
1655
+
1656
+ sage: C = CombinatorialPolyhedron(P.incidence_matrix())
1657
+ sage: C.vertex_facet_graph().vertices(sort=True) # needs sage.graphs
1658
+ [('H', 0),
1659
+ ('H', 1),
1660
+ ('H', 2),
1661
+ ('H', 3),
1662
+ ('H', 4),
1663
+ ('V', 0),
1664
+ ('V', 1),
1665
+ ('V', 2),
1666
+ ('V', 3),
1667
+ ('V', 4)]
1668
+
1669
+ If ``names`` is ``False`` then the vertices of the graph are given by integers::
1670
+
1671
+ sage: C.vertex_facet_graph(names=False).vertices(sort=True) # needs sage.graphs
1672
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1673
+
1674
+ TESTS:
1675
+
1676
+ Test that :issue:`29898` is fixed::
1677
+
1678
+ sage: Polyhedron().vertex_facet_graph() # needs sage.graphs
1679
+ Digraph on 0 vertices
1680
+ sage: Polyhedron([[0]]).vertex_facet_graph() # needs sage.graphs
1681
+ Digraph on 1 vertex
1682
+ sage: Polyhedron([[0]]).vertex_facet_graph(False) # needs sage.graphs
1683
+ Digraph on 1 vertex
1684
+ """
1685
+ from sage.graphs.digraph import DiGraph
1686
+ if self.dimension() == -1:
1687
+ return DiGraph()
1688
+ if self.dimension() == 0:
1689
+ if not names:
1690
+ return DiGraph(1)
1691
+ else:
1692
+ Vrep = self.Vrep()
1693
+ if Vrep:
1694
+ v = Vrep[0]
1695
+ else:
1696
+ v = ("V", 0)
1697
+ return DiGraph([[v], []])
1698
+
1699
+ # The face iterator will iterate through the facets in opposite order.
1700
+ facet_iter = self.face_iter(self.dimension() - 1, algorithm='primal')
1701
+ n_facets = self.n_facets()
1702
+ n_Vrep = self.n_Vrepresentation()
1703
+
1704
+ if not names:
1705
+ vertices = [i for i in range(n_facets + n_Vrep)]
1706
+ edges = tuple((j, n_Vrep + n_facets - 1 - i) for i, facet in enumerate(facet_iter) for j in facet.ambient_V_indices())
1707
+ else:
1708
+ facet_names = self.facet_names()
1709
+ if facet_names is None:
1710
+ # No names were provided at initialisation.
1711
+ facet_names = [("H", i) for i in range(n_facets)]
1712
+
1713
+ Vrep = self.Vrep()
1714
+ if Vrep is None:
1715
+ # No names were provided at initialisation.
1716
+ Vrep = [("V", i) for i in range(n_Vrep)]
1717
+
1718
+ vertices = Vrep + facet_names
1719
+ edges = tuple((Vrep[j], facet_names[n_facets - 1 - i]) for i, facet in enumerate(facet_iter) for j in facet.ambient_V_indices())
1720
+ return DiGraph([vertices, edges], format='vertices_and_edges', immutable=True)
1721
+
1722
+ @cached_method
1723
+ def f_vector(self, num_threads=None, parallelization_depth=None, algorithm=None):
1724
+ r"""
1725
+ Compute the ``f_vector`` of the polyhedron.
1726
+
1727
+ The ``f_vector`` contains the number of faces of dimension `k`
1728
+ for each `k` in ``range(-1, self.dimension() + 1)``.
1729
+
1730
+ INPUT:
1731
+
1732
+ - ``num_threads`` -- integer (optional); specify the number of threads
1733
+
1734
+ - ``parallelization_depth`` -- integer (optional); specify
1735
+ how deep in the lattice the parallelization is done
1736
+
1737
+ - ``algorithm`` -- string (optional);
1738
+ specify whether the face generator starts with facets or vertices:
1739
+
1740
+ * ``'primal'`` -- start with the facets
1741
+ * ``'dual'`` -- start with the vertices
1742
+ * ``None`` -- choose automatically
1743
+
1744
+ .. NOTE::
1745
+
1746
+ To obtain edges and/or ridges as well, first do so. This might
1747
+ already compute the ``f_vector``.
1748
+
1749
+ EXAMPLES::
1750
+
1751
+ sage: P = polytopes.permutahedron(5)
1752
+ sage: C = CombinatorialPolyhedron(P)
1753
+ sage: C.f_vector()
1754
+ (1, 120, 240, 150, 30, 1)
1755
+
1756
+ sage: P = polytopes.cyclic_polytope(6,10)
1757
+ sage: C = CombinatorialPolyhedron(P)
1758
+ sage: C.f_vector()
1759
+ (1, 10, 45, 120, 185, 150, 50, 1)
1760
+
1761
+ Using two threads::
1762
+
1763
+ sage: P = polytopes.permutahedron(5)
1764
+ sage: C = CombinatorialPolyhedron(P)
1765
+ sage: C.f_vector(num_threads=2)
1766
+ (1, 120, 240, 150, 30, 1)
1767
+
1768
+ TESTS::
1769
+
1770
+ sage: type(C.f_vector())
1771
+ <class 'sage.modules.vector_integer_dense.Vector_integer_dense'>
1772
+ """
1773
+ if num_threads is None:
1774
+ from sage.parallel.ncpus import ncpus
1775
+ num_threads = ncpus()
1776
+
1777
+ if parallelization_depth is None:
1778
+ # Setting some reasonable defaults.
1779
+ if num_threads == 0:
1780
+ parallelization_depth = 0
1781
+ elif num_threads <= 3:
1782
+ parallelization_depth = 1
1783
+ elif num_threads <= 8:
1784
+ parallelization_depth = 2
1785
+ else:
1786
+ parallelization_depth = 3
1787
+
1788
+ if not self._f_vector:
1789
+ self._compute_f_vector(num_threads, parallelization_depth, self._algorithm_to_dual(algorithm))
1790
+ if not self._f_vector:
1791
+ raise ValueError("could not determine f_vector")
1792
+ from sage.modules.free_module_element import vector
1793
+ from sage.rings.integer_ring import ZZ
1794
+ f_vector = vector(ZZ, self._f_vector)
1795
+ f_vector.set_immutable()
1796
+ return f_vector
1797
+
1798
+ def flag_f_vector(self, *args):
1799
+ r"""
1800
+ Return the flag f-vector.
1801
+
1802
+ For each `-1 < i_0 < \dots < i_n < d` the flag f-vector
1803
+ counts the number of flags `F_0 \subset \dots \subset F_n`
1804
+ with `F_j` of dimension `i_j` for each `0 \leq j \leq n`,
1805
+ where `d` is the dimension of the polyhedron.
1806
+
1807
+ INPUT:
1808
+
1809
+ - ``args`` -- integer (optional); specify an entry of the
1810
+ flag-f-vector (must be an increasing sequence of integers)
1811
+
1812
+ OUTPUT:
1813
+
1814
+ - a dictionary, if no arguments were given
1815
+
1816
+ - an integer, if arguments were given
1817
+
1818
+ EXAMPLES:
1819
+
1820
+ Obtain the entire flag-f-vector::
1821
+
1822
+ sage: C = polytopes.hypercube(4).combinatorial_polyhedron()
1823
+ sage: C.flag_f_vector() # needs sage.combinat
1824
+ {(-1,): 1,
1825
+ (0,): 16,
1826
+ (0, 1): 64,
1827
+ (0, 1, 2): 192,
1828
+ (0, 1, 2, 3): 384,
1829
+ (0, 1, 3): 192,
1830
+ (0, 2): 96,
1831
+ (0, 2, 3): 192,
1832
+ (0, 3): 64,
1833
+ (1,): 32,
1834
+ (1, 2): 96,
1835
+ (1, 2, 3): 192,
1836
+ (1, 3): 96,
1837
+ (2,): 24,
1838
+ (2, 3): 48,
1839
+ (3,): 8,
1840
+ (4,): 1}
1841
+
1842
+ Specify an entry::
1843
+
1844
+ sage: C.flag_f_vector(0,3) # needs sage.combinat
1845
+ 64
1846
+ sage: C.flag_f_vector(2) # needs sage.combinat
1847
+ 24
1848
+
1849
+ Leading ``-1`` and trailing entry of dimension are allowed::
1850
+
1851
+ sage: C.flag_f_vector(-1,0,3) # needs sage.combinat
1852
+ 64
1853
+ sage: C.flag_f_vector(-1,0,3,4) # needs sage.combinat
1854
+ 64
1855
+
1856
+ One can get the number of trivial faces::
1857
+
1858
+ sage: C.flag_f_vector(-1) # needs sage.combinat
1859
+ 1
1860
+ sage: C.flag_f_vector(4) # needs sage.combinat
1861
+ 1
1862
+
1863
+ Polyhedra with lines, have ``0`` entries accordingly::
1864
+
1865
+ sage: C = (Polyhedron(lines=[[1]]) * polytopes.hypercube(2)).combinatorial_polyhedron()
1866
+ sage: C.flag_f_vector() # needs sage.combinat
1867
+ {(-1,): 1, (0, 1): 0, (0, 2): 0, (0,): 0, (1, 2): 8, (1,): 4, (2,): 4, 3: 1}
1868
+
1869
+ If the arguments are not strictly increasing or out of range,
1870
+ a key error is raised::
1871
+
1872
+ sage: C.flag_f_vector(-1,0,3,5) # needs sage.combinat
1873
+ Traceback (most recent call last):
1874
+ ...
1875
+ KeyError: (0, 3, 5)
1876
+ sage: C.flag_f_vector(-1,3,0) # needs sage.combinat
1877
+ Traceback (most recent call last):
1878
+ ...
1879
+ KeyError: (3, 0)
1880
+ """
1881
+ flag = self._flag_f_vector()
1882
+ if len(args) == 0:
1883
+ return flag
1884
+ elif len(args) == 1:
1885
+ return flag[(args[0],)]
1886
+ else:
1887
+ dim = self.dimension()
1888
+ if args[0] == -1:
1889
+ args = args[1:]
1890
+ if args[-1] == dim:
1891
+ args = args[:-1]
1892
+ return flag[tuple(args)]
1893
+
1894
+ @cached_method
1895
+ def _flag_f_vector(self):
1896
+ r"""
1897
+ Obtain the flag-f-vector from the flag-f-polynomial from the face lattice.
1898
+
1899
+ See :meth:`flag_f_vector`.
1900
+
1901
+ TESTS::
1902
+
1903
+ sage: C = CombinatorialPolyhedron(3)
1904
+ sage: C._flag_f_vector() # needs sage.combinat
1905
+ {(-1,): 1, (0, 1): 0, (0, 2): 0, (0,): 0, (1, 2): 0, (1,): 0, (2,): 0, 3: 1}
1906
+ """
1907
+ poly = self.face_lattice().flag_f_polynomial()
1908
+ variables = poly.variables()
1909
+ dim = self.dimension()
1910
+ flag = {(smallInteger(-1),): smallInteger(1)}
1911
+ for term in poly.monomials():
1912
+ index = tuple([variables.index(var) for var in term.variables()[:-1]])
1913
+ if index == ():
1914
+ flag[(dim,)] = smallInteger(1)
1915
+ else:
1916
+ flag[index] = poly.monomial_coefficient(term)
1917
+
1918
+ n_lines = sum([1 for x in self.f_vector() if x == 0])
1919
+ if n_lines:
1920
+ # The polyhedron has lines and we have to account for that.
1921
+ # So we basically shift all entries up by the number of lines
1922
+ # and add zero entries for the lines.
1923
+ from itertools import combinations
1924
+ flag_old = flag
1925
+ flag = {(smallInteger(-1),): smallInteger(1)}
1926
+ ran = [smallInteger(i) for i in range(self.dim())]
1927
+ for k in range(1, self.dim()):
1928
+ for comb in combinations(ran, self.dim() - k):
1929
+ if comb[0] < n_lines:
1930
+ # There are no faces of dimension 0,...,n_lines.
1931
+ flag[comb] = smallInteger(0)
1932
+ else:
1933
+ # Shift the old entries up by the number of lines.
1934
+ flag[comb] = flag_old[tuple(i - n_lines for i in comb)]
1935
+
1936
+ flag[self.dimension()] = smallInteger(1)
1937
+
1938
+ return flag
1939
+
1940
+ @cached_method
1941
+ def neighborliness(self):
1942
+ r"""
1943
+ Return the largest ``k``, such that the polyhedron is ``k``-neighborly.
1944
+
1945
+ A polyhedron is `k`-neighborly if every set of `n` vertices forms a face
1946
+ for `n` up to `k`.
1947
+
1948
+ In case of the `d`-dimensional simplex, it returns `d + 1`.
1949
+
1950
+ .. SEEALSO::
1951
+
1952
+ :meth:`is_neighborly`
1953
+
1954
+ EXAMPLES::
1955
+
1956
+ sage: P = polytopes.cyclic_polytope(8,12)
1957
+ sage: C = P.combinatorial_polyhedron()
1958
+ sage: C.neighborliness()
1959
+ 4
1960
+ sage: P = polytopes.simplex(6)
1961
+ sage: C = P.combinatorial_polyhedron()
1962
+ sage: C.neighborliness()
1963
+ 7
1964
+ sage: P = polytopes.cyclic_polytope(4,10)
1965
+ sage: P = P.join(P)
1966
+ sage: C = P.combinatorial_polyhedron()
1967
+ sage: C.neighborliness()
1968
+ 2
1969
+ """
1970
+ if self.is_simplex():
1971
+ return self.dim() + 1
1972
+ cdef int k = 2
1973
+ f = self.f_vector()
1974
+ while f[k] == self.n_vertices().binomial(k):
1975
+ k += 1
1976
+ return k - 1
1977
+
1978
+ @cached_method
1979
+ def is_neighborly(self, k=None) -> bool:
1980
+ r"""
1981
+ Return whether the polyhedron is neighborly.
1982
+
1983
+ If the input `k` is provided, then return whether the polyhedron is `k`-neighborly.
1984
+
1985
+ A polyhedron is neighborly if every set of `n` vertices forms a face
1986
+ for `n` up to floor of half the dimension of the polyhedron.
1987
+ It is `k`-neighborly if this is true for `n` up to `k`.
1988
+
1989
+ INPUT:
1990
+
1991
+ - ``k`` -- the dimension up to which to check if every set of ``k``
1992
+ vertices forms a face. If no ``k`` is provided, check up to floor
1993
+ of half the dimension of the polyhedron.
1994
+
1995
+ OUTPUT:
1996
+
1997
+ - ``True`` if the every set of up to ``k`` vertices forms a face,
1998
+ - ``False`` otherwise
1999
+
2000
+ .. SEEALSO::
2001
+
2002
+ :meth:`neighborliness`
2003
+
2004
+ EXAMPLES::
2005
+
2006
+ sage: P = polytopes.cyclic_polytope(8,12)
2007
+ sage: C = P.combinatorial_polyhedron()
2008
+ sage: C.is_neighborly()
2009
+ True
2010
+ sage: P = polytopes.simplex(6)
2011
+ sage: C = P.combinatorial_polyhedron()
2012
+ sage: C.is_neighborly()
2013
+ True
2014
+ sage: P = polytopes.cyclic_polytope(4,10)
2015
+ sage: P = P.join(P)
2016
+ sage: C = P.combinatorial_polyhedron()
2017
+ sage: C.is_neighborly()
2018
+ False
2019
+ sage: C.is_neighborly(k=2)
2020
+ True
2021
+ """
2022
+ from sage.arith.misc import binomial
2023
+ if k is None:
2024
+ k = self.dim() // 2
2025
+ return all(self.f_vector()[i+1] == binomial(self.n_vertices(), i + 1)
2026
+ for i in range(1, k))
2027
+
2028
+ def is_simplex(self) -> bool:
2029
+ r"""
2030
+ Return whether the polyhedron is a simplex.
2031
+
2032
+ A simplex is a bounded polyhedron with `d+1` vertices, where
2033
+ `d` is the dimension.
2034
+
2035
+ EXAMPLES::
2036
+
2037
+ sage: CombinatorialPolyhedron(2).is_simplex()
2038
+ False
2039
+ sage: CombinatorialPolyhedron([[0,1],[0,2],[1,2]]).is_simplex()
2040
+ True
2041
+ """
2042
+ return self.is_bounded() and (self.dim() + 1 == self.n_vertices())
2043
+
2044
+ @cached_method
2045
+ def is_simplicial(self) -> bool:
2046
+ r"""
2047
+ Test whether the polytope is simplicial.
2048
+
2049
+ This method is not implemented for unbounded polyhedra.
2050
+
2051
+ A polytope is simplicial, if each facet contains exactly `d` vertices,
2052
+ where `d` is the dimension of the polytope.
2053
+
2054
+ EXAMPLES::
2055
+
2056
+ sage: P = polytopes.cyclic_polytope(4,10)
2057
+ sage: C = P.combinatorial_polyhedron()
2058
+ sage: C.is_simplicial()
2059
+ True
2060
+ sage: P = polytopes.hypercube(4)
2061
+ sage: C = P.combinatorial_polyhedron()
2062
+ sage: C.is_simplicial()
2063
+ False
2064
+
2065
+ For unbounded polyhedra, an error is raised::
2066
+
2067
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
2068
+ sage: C.is_simplicial()
2069
+ Traceback (most recent call last):
2070
+ ...
2071
+ NotImplementedError: this function is implemented for polytopes only
2072
+ """
2073
+ if not self.is_bounded():
2074
+ raise NotImplementedError("this function is implemented for polytopes only")
2075
+
2076
+ cdef ListOfFaces facets = self._bitrep_facets
2077
+ cdef size_t n_facets = facets.n_faces()
2078
+ cdef size_t i
2079
+ cdef int dim = self.dimension()
2080
+
2081
+ for i in range(n_facets):
2082
+ if face_len_atoms(facets.data.faces[i]) != dim:
2083
+ return False
2084
+ return True
2085
+
2086
+ @cached_method
2087
+ def simpliciality(self):
2088
+ r"""
2089
+ Return the largest `k` such that the polytope is `k`-simplicial.
2090
+
2091
+ Return the dimension in case of a simplex.
2092
+
2093
+ A polytope is `k`-simplicial, if every `k`-face is a simplex.
2094
+
2095
+ EXAMPLES::
2096
+
2097
+ sage: cyclic = polytopes.cyclic_polytope(10,4)
2098
+ sage: CombinatorialPolyhedron(cyclic).simpliciality()
2099
+ 3
2100
+
2101
+ sage: hypersimplex = polytopes.hypersimplex(5,2)
2102
+ sage: CombinatorialPolyhedron(hypersimplex).simpliciality()
2103
+ 2
2104
+
2105
+ sage: cross = polytopes.cross_polytope(4)
2106
+ sage: P = cross.join(cross)
2107
+ sage: CombinatorialPolyhedron(P).simpliciality()
2108
+ 3
2109
+
2110
+ sage: P = polytopes.simplex(3)
2111
+ sage: CombinatorialPolyhedron(P).simpliciality()
2112
+ 3
2113
+
2114
+ sage: P = polytopes.simplex(1)
2115
+ sage: CombinatorialPolyhedron(P).simpliciality()
2116
+ 1
2117
+
2118
+ TESTS::
2119
+
2120
+ sage: P = polytopes.cube()
2121
+ sage: C = CombinatorialPolyhedron(P)
2122
+ sage: C.simpliciality is C.simpliciality
2123
+ True
2124
+ """
2125
+ if not self.is_bounded():
2126
+ raise NotImplementedError("must be bounded")
2127
+ cdef FaceIterator face_iter = self._face_iter(False, -2)
2128
+ cdef int d
2129
+ cdef int dim = self.dimension()
2130
+
2131
+ if self.n_facets() == self.dimension() + 1:
2132
+ # A simplex.
2133
+ return self.dimension()
2134
+
2135
+ cdef simpliciality = dim - 1
2136
+
2137
+ # For each face in the iterator, check if its a simplex.
2138
+ face_iter.structure.lowest_dimension = 2 # every 1-face is a simplex
2139
+ d = face_iter.next_dimension()
2140
+ while d < dim:
2141
+ sig_check()
2142
+ if face_iter.n_atom_rep() == d + 1:
2143
+ # The current face is a simplex.
2144
+ face_iter.ignore_subfaces()
2145
+ else:
2146
+ # Current face is not a simplex.
2147
+ if simpliciality > d - 1:
2148
+ simpliciality = d - 1
2149
+ d = face_iter.next_dimension()
2150
+ if simpliciality == 1:
2151
+ # Every polytope is 1-simplicial.
2152
+ d = dim
2153
+ return smallInteger(simpliciality)
2154
+
2155
+ @cached_method
2156
+ def is_simple(self):
2157
+ r"""
2158
+ Test whether the polytope is simple.
2159
+
2160
+ If the polyhedron is unbounded, return ``False``.
2161
+
2162
+ A polytope is simple, if each vertex is contained in exactly `d` facets,
2163
+ where `d` is the dimension of the polytope.
2164
+
2165
+ EXAMPLES::
2166
+
2167
+ sage: P = polytopes.cyclic_polytope(4,10)
2168
+ sage: C = P.combinatorial_polyhedron()
2169
+ sage: C.is_simple()
2170
+ False
2171
+ sage: P = polytopes.hypercube(4)
2172
+ sage: C = P.combinatorial_polyhedron()
2173
+ sage: C.is_simple()
2174
+ True
2175
+
2176
+ Return ``False`` for unbounded polyhedra::
2177
+
2178
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
2179
+ sage: C.is_simple()
2180
+ False
2181
+ """
2182
+ if not self.is_bounded():
2183
+ return False
2184
+
2185
+ cdef ListOfFaces vertices = self._bitrep_Vrep
2186
+ cdef size_t n_vertices = vertices.n_faces()
2187
+ cdef size_t i
2188
+ cdef int dim = self.dimension()
2189
+
2190
+ for i in range(n_vertices):
2191
+ if face_len_atoms(vertices.data.faces[i]) != dim:
2192
+ return False
2193
+ return True
2194
+
2195
+ @cached_method
2196
+ def simplicity(self):
2197
+ r"""
2198
+ Return the largest `k` such that the polytope is `k`-simple.
2199
+
2200
+ Return the dimension in case of a simplex.
2201
+
2202
+ A polytope `P` is `k`-simple, if every `(d-1-k)`-face
2203
+ is contained in exactly `k+1` facets of `P` for `1 \leq k \leq d-1`.
2204
+
2205
+ Equivalently it is `k`-simple if the polar/dual polytope is `k`-simplicial.
2206
+
2207
+ EXAMPLES::
2208
+
2209
+ sage: hyper4 = polytopes.hypersimplex(4,2)
2210
+ sage: CombinatorialPolyhedron(hyper4).simplicity()
2211
+ 1
2212
+
2213
+ sage: hyper5 = polytopes.hypersimplex(5,2)
2214
+ sage: CombinatorialPolyhedron(hyper5).simplicity()
2215
+ 2
2216
+
2217
+ sage: hyper6 = polytopes.hypersimplex(6,2)
2218
+ sage: CombinatorialPolyhedron(hyper6).simplicity()
2219
+ 3
2220
+
2221
+ sage: P = polytopes.simplex(3)
2222
+ sage: CombinatorialPolyhedron(P).simplicity()
2223
+ 3
2224
+
2225
+ sage: P = polytopes.simplex(1)
2226
+ sage: CombinatorialPolyhedron(P).simplicity()
2227
+ 1
2228
+
2229
+ TESTS::
2230
+
2231
+ sage: P = polytopes.cube()
2232
+ sage: C = CombinatorialPolyhedron(P)
2233
+ sage: C.simplicity is C.simplicity
2234
+ True
2235
+ """
2236
+ if not self.is_bounded():
2237
+ raise NotImplementedError("must be bounded")
2238
+ cdef FaceIterator coface_iter = self._face_iter(True, -2)
2239
+ cdef int d
2240
+ cdef int dim = self.dimension()
2241
+
2242
+ if self.n_facets() == self.dimension() + 1:
2243
+ # A simplex.
2244
+ return self.dimension()
2245
+
2246
+ cdef simplicity = dim - 1
2247
+
2248
+ # For each coface in the iterator, check if its a simplex.
2249
+ coface_iter.structure.lowest_dimension = 2 # every coface of dimension 1 is a simplex
2250
+ d = coface_iter.next_dimension()
2251
+ while d < dim:
2252
+ sig_check()
2253
+ if coface_iter.n_atom_rep() == d + 1:
2254
+ # The current coface is a simplex.
2255
+ coface_iter.ignore_supfaces()
2256
+ else:
2257
+ # Current coface is not a simplex.
2258
+ if simplicity > d - 1:
2259
+ simplicity = d - 1
2260
+ d = coface_iter.next_dimension()
2261
+ if simplicity == 1:
2262
+ # Every polytope is 1-simple.
2263
+ d = dim
2264
+ return smallInteger(simplicity)
2265
+
2266
+ @cached_method
2267
+ def is_lawrence_polytope(self):
2268
+ r"""
2269
+ Return ``True`` if ``self`` is a Lawrence polytope.
2270
+
2271
+ A polytope is called a Lawrence polytope if it has a centrally
2272
+ symmetric (normalized) Gale diagram.
2273
+
2274
+ Equivalently, there exists a partition `P_1,\dots,P_k`
2275
+ of the vertices `V` such that each part
2276
+ `P_i` has size `2` or `1` and for each part there exists
2277
+ a facet with vertices exactly `V \setminus P_i`.
2278
+
2279
+ EXAMPLES::
2280
+
2281
+ sage: C = polytopes.simplex(5).combinatorial_polyhedron()
2282
+ sage: C.is_lawrence_polytope()
2283
+ True
2284
+ sage: P = polytopes.hypercube(4).lawrence_polytope()
2285
+ sage: C = P.combinatorial_polyhedron()
2286
+ sage: C.is_lawrence_polytope()
2287
+ True
2288
+ sage: P = polytopes.hypercube(4)
2289
+ sage: C = P.combinatorial_polyhedron()
2290
+ sage: C.is_lawrence_polytope()
2291
+ False
2292
+
2293
+ For unbounded polyhedra, an error is raised::
2294
+
2295
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
2296
+ sage: C.is_lawrence_polytope()
2297
+ Traceback (most recent call last):
2298
+ ...
2299
+ NotImplementedError: this function is implemented for polytopes only
2300
+
2301
+ AUTHORS:
2302
+
2303
+ - Laith Rastanawi
2304
+ - Jonathan Kliem
2305
+
2306
+ REFERENCES:
2307
+
2308
+ For more information, see [BaSt1990]_.
2309
+ """
2310
+ if not self.is_compact():
2311
+ raise NotImplementedError("this function is implemented for polytopes only")
2312
+ if self.n_Vrepresentation() <= 2:
2313
+ return True
2314
+
2315
+ cdef FaceIterator facet_iterator = self._face_iter(False, self.dimension()-1)
2316
+ cdef CombinatorialFace facet
2317
+ cdef size_t n_vertices = self.n_Vrepresentation()
2318
+ cdef size_t one, two, length, counter
2319
+ cdef list vertices = [1 for _ in range(n_vertices)]
2320
+
2321
+ for facet in facet_iterator:
2322
+ length = facet.n_atom_rep()
2323
+ if length >= n_vertices - 2:
2324
+ # The facet has at most two non-vertices and corresponds to
2325
+ # two symmetric vertices or a vertex at the origin
2326
+ # in the Gale transform.
2327
+ facet.set_atom_rep()
2328
+ counter = 0
2329
+ while counter < length:
2330
+ if facet.atom_rep[counter] != counter:
2331
+ # We have found our first non-vertex.
2332
+ one = counter
2333
+ break
2334
+ counter += 1
2335
+ else:
2336
+ # The facet contains the first ``length`` vertices.
2337
+ one = length
2338
+
2339
+ if length == n_vertices - 1:
2340
+ # The facet corresponds to a vertex at the origin
2341
+ # of the Gale transform.
2342
+ vertices[one] = 0
2343
+ else:
2344
+ # The facet corresponds to two symmetric vertices
2345
+ # of the Gale transform.
2346
+ while counter < length:
2347
+ if facet.atom_rep[counter] != counter + 1:
2348
+ # We have found our second non-vertex.
2349
+ two = counter + 1
2350
+ break
2351
+ counter += 1
2352
+ else:
2353
+ # The second non-vertex is the very last vertex.
2354
+ two = length + 1
2355
+
2356
+ if vertices[one] == vertices[two]:
2357
+ # Possibly the Gale transform contains duplicates,
2358
+ # we must make sure that the mulitplicites are symmetric as well.
2359
+ # (And not two vertices are symmetric to just one).
2360
+ vertices[one] = 0
2361
+ vertices[two] = 0
2362
+
2363
+ return not any(vertices)
2364
+
2365
+ @cached_method
2366
+ def is_pyramid(self, certificate=False):
2367
+ r"""
2368
+ Test whether the polytope is a pyramid over one of its facets.
2369
+
2370
+ INPUT:
2371
+
2372
+ - ``certificate`` -- boolean (default: ``False``); specifies whether
2373
+ to return a vertex of the polytope which is the apex of a pyramid,
2374
+ if found
2375
+
2376
+ OUTPUT:
2377
+
2378
+ If ``certificate`` is ``True``, returns a tuple containing:
2379
+
2380
+ 1. Boolean.
2381
+ 2. The apex of the pyramid or ``None``.
2382
+
2383
+ If ``certificate`` is ``False`` returns a boolean.
2384
+
2385
+ AUTHORS:
2386
+
2387
+ - Laith Rastanawi
2388
+ - Jonathan Kliem
2389
+
2390
+ EXAMPLES::
2391
+
2392
+ sage: C = polytopes.cross_polytope(4).combinatorial_polyhedron()
2393
+ sage: C.is_pyramid()
2394
+ False
2395
+ sage: C.is_pyramid(certificate=True)
2396
+ (False, None)
2397
+ sage: C = polytopes.cross_polytope(4).pyramid().combinatorial_polyhedron()
2398
+ sage: C.is_pyramid()
2399
+ True
2400
+ sage: C.is_pyramid(certificate=True)
2401
+ (True, A vertex at (1, 0, 0, 0, 0))
2402
+ sage: C = polytopes.simplex(5).combinatorial_polyhedron()
2403
+ sage: C.is_pyramid(certificate=True)
2404
+ (True, A vertex at (1, 0, 0, 0, 0, 0))
2405
+
2406
+ For unbounded polyhedra, an error is raised::
2407
+
2408
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
2409
+ sage: C.is_pyramid()
2410
+ Traceback (most recent call last):
2411
+ ...
2412
+ ValueError: polyhedron has to be compact
2413
+
2414
+ TESTS::
2415
+
2416
+ sage: CombinatorialPolyhedron(-1).is_pyramid()
2417
+ False
2418
+ sage: CombinatorialPolyhedron(-1).is_pyramid(True)
2419
+ (False, None)
2420
+ sage: CombinatorialPolyhedron(0).is_pyramid()
2421
+ True
2422
+ sage: CombinatorialPolyhedron(0).is_pyramid(True)
2423
+ (True, 0)
2424
+
2425
+ Check that :issue:`30292` is fixed::
2426
+
2427
+ sage: Polyhedron([[0, -1, -1], [0, -1, 1], [0, 1, -1], [0, 1, 1], [1, 0, 0]]).is_pyramid(certificate=True)
2428
+ (True, A vertex at (1, 0, 0))
2429
+ """
2430
+ if not self.is_bounded():
2431
+ raise ValueError("polyhedron has to be compact")
2432
+
2433
+ if self.dim() == -1:
2434
+ if certificate:
2435
+ return (False, None)
2436
+ return False
2437
+
2438
+ if self.dim() == 0:
2439
+ if certificate:
2440
+ return (True, self.Vrepresentation()[0])
2441
+ return True
2442
+
2443
+ # Find a vertex that is incident to all elements in Hrepresentation but one.
2444
+ vertex_iter = self._face_iter(True, 0)
2445
+ n_facets = self.n_facets()
2446
+ for vertex in vertex_iter:
2447
+ if vertex.n_ambient_Hrepresentation(add_equations=False) == n_facets - 1:
2448
+ if certificate:
2449
+ return (True, vertex.ambient_Vrepresentation()[0])
2450
+ return True
2451
+
2452
+ if certificate:
2453
+ return (False, None)
2454
+ return False
2455
+
2456
+ @cached_method
2457
+ def is_bipyramid(self, certificate=False):
2458
+ r"""
2459
+ Test whether the polytope is a bipyramid over some other polytope.
2460
+
2461
+ INPUT:
2462
+
2463
+ - ``certificate`` -- boolean (default: ``False``); specifies whether
2464
+ to return a vertex of the polytope which is the apex of a pyramid,
2465
+ if found
2466
+
2467
+ INPUT:
2468
+
2469
+ - ``certificate`` -- boolean (default: ``False``); specifies whether
2470
+ to return two vertices of the polytope which are the apices of a
2471
+ bipyramid, if found
2472
+
2473
+ OUTPUT:
2474
+
2475
+ If ``certificate`` is ``True``, returns a tuple containing:
2476
+
2477
+ 1. Boolean.
2478
+ 2. ``None`` or a tuple containing:
2479
+ a. The first apex.
2480
+ b. The second apex.
2481
+
2482
+ If ``certificate`` is ``False`` returns a boolean.
2483
+
2484
+ EXAMPLES::
2485
+
2486
+ sage: C = polytopes.hypercube(4).combinatorial_polyhedron()
2487
+ sage: C.is_bipyramid()
2488
+ False
2489
+ sage: C.is_bipyramid(certificate=True)
2490
+ (False, None)
2491
+ sage: C = polytopes.cross_polytope(4).combinatorial_polyhedron()
2492
+ sage: C.is_bipyramid()
2493
+ True
2494
+ sage: C.is_bipyramid(certificate=True)
2495
+ (True, [A vertex at (1, 0, 0, 0), A vertex at (-1, 0, 0, 0)])
2496
+
2497
+ For unbounded polyhedra, an error is raised::
2498
+
2499
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
2500
+ sage: C.is_pyramid()
2501
+ Traceback (most recent call last):
2502
+ ...
2503
+ ValueError: polyhedron has to be compact
2504
+
2505
+ TESTS::
2506
+
2507
+ sage: CombinatorialPolyhedron(-1).is_bipyramid()
2508
+ False
2509
+ sage: CombinatorialPolyhedron(-1).is_bipyramid(True)
2510
+ (False, None)
2511
+ sage: C = polytopes.cross_polytope(1)
2512
+ sage: C.is_bipyramid()
2513
+ True
2514
+ sage: C.is_bipyramid(True)
2515
+ (True, [A vertex at (1), A vertex at (-1)])
2516
+
2517
+ Check that bug analog to :issue:`30292` is avoided::
2518
+
2519
+ sage: Polyhedron([[0, 1, 0], [0, 0, 1], [0, -1, -1], [1, 0, 0], [-1, 0, 0]]).is_bipyramid(certificate=True)
2520
+ (True, [A vertex at (1, 0, 0), A vertex at (-1, 0, 0)])
2521
+
2522
+ ALGORITHM:
2523
+
2524
+ Assume all faces of a polyhedron to be given as lists of vertices.
2525
+
2526
+ A polytope is a bipyramid with apexes `v`, `w` if and only if for each
2527
+ proper face `v \in F` there exists a face `G` with
2528
+ `G \setminus \{w\} = F \setminus \{v\}`
2529
+ and vice versa (for each proper face
2530
+ `w \in F` there exists ...).
2531
+
2532
+ To check this property it suffices to check for all facets of the polyhedron.
2533
+ """
2534
+ if not self.is_compact():
2535
+
2536
+ raise ValueError("polyhedron has to be compact")
2537
+
2538
+ n_facets = self.n_facets()
2539
+ if n_facets % 2 or self.dim() < 1:
2540
+ if certificate:
2541
+ return (False, None)
2542
+ return False
2543
+
2544
+ facets_incidences = [set(f) for f in self.facets(names=False)]
2545
+ verts_incidences = dict()
2546
+ for v in self.face_iter(0):
2547
+ verts_incidences[v.ambient_V_indices()[0]] = set(v.ambient_H_indices(add_equations=False))
2548
+
2549
+ # Find two vertices ``vert1`` and ``vert2`` such that one of them
2550
+ # lies on exactly half of the facets, and the other one lies on
2551
+ # exactly the other half.
2552
+ from itertools import combinations
2553
+ for index1, index2 in combinations(verts_incidences, 2):
2554
+ vert1_incidences = verts_incidences[index1]
2555
+ vert2_incidences = verts_incidences[index2]
2556
+ vert1and2 = vert1_incidences.union(vert2_incidences)
2557
+ if len(vert1and2) == n_facets:
2558
+ # We have found two candidates for apexes.
2559
+ # Remove from each facet ``index1`` resp. ``index2``.
2560
+ test_facets = set(frozenset(facet_inc.difference({index1, index2}))
2561
+ for facet_inc in facets_incidences)
2562
+ if len(test_facets) == n_facets/2:
2563
+ # For each `F` containing `index1` there is
2564
+ # `G` containing `index2` such that
2565
+ # `F \setminus \{index1\} = G \setminus \{index2\}
2566
+ # and vice versa.
2567
+ if certificate:
2568
+ V = self.vertices()
2569
+ return (True, [V[index1], V[index2]])
2570
+ return True
2571
+
2572
+ if certificate:
2573
+ return (False, None)
2574
+ return False
2575
+
2576
+ @cached_method
2577
+ def is_prism(self, certificate=False):
2578
+ r"""
2579
+ Test whether the polytope is a prism of some polytope.
2580
+
2581
+ INPUT:
2582
+
2583
+ - ``certificate`` -- boolean (default: ``False``); specifies whether
2584
+ to return two facets of the polytope which are the bases of a prism,
2585
+ if found
2586
+
2587
+ OUTPUT:
2588
+
2589
+ If ``certificate`` is ``True``, returns a tuple containing:
2590
+
2591
+ 1. Boolean.
2592
+ 2. ``None`` or a tuple containing:
2593
+ a. List of the vertices of the first base facet.
2594
+ b. List of the vertices of the second base facet.
2595
+
2596
+ If ``certificate`` is ``False`` returns a boolean.
2597
+
2598
+ TESTS::
2599
+
2600
+ sage: CombinatorialPolyhedron(-1).is_prism()
2601
+ False
2602
+ sage: CombinatorialPolyhedron(1).is_prism()
2603
+ False
2604
+ sage: C = polytopes.cross_polytope(3).prism().combinatorial_polyhedron()
2605
+ sage: C.is_prism(certificate=True)
2606
+ (True,
2607
+ [(A vertex at (0, 0, 1, 0),
2608
+ A vertex at (0, 1, 0, 0),
2609
+ A vertex at (0, 0, 0, -1),
2610
+ A vertex at (0, 0, -1, 0),
2611
+ A vertex at (0, -1, 0, 0),
2612
+ A vertex at (0, 0, 0, 1)),
2613
+ (A vertex at (1, 1, 0, 0),
2614
+ A vertex at (1, 0, 0, -1),
2615
+ A vertex at (1, 0, -1, 0),
2616
+ A vertex at (1, -1, 0, 0),
2617
+ A vertex at (1, 0, 0, 1),
2618
+ A vertex at (1, 0, 1, 0))])
2619
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
2620
+ sage: C.is_prism()
2621
+ Traceback (most recent call last):
2622
+ ...
2623
+ ValueError: self must be bounded
2624
+ """
2625
+ if not certificate:
2626
+ return self.dual().is_bipyramid()
2627
+
2628
+ val, cert = self.dual().is_bipyramid(True)
2629
+ if val:
2630
+ facets = self.facets()
2631
+ return (True, [facets[cert[0]], facets[cert[1]]])
2632
+
2633
+ return (False, None)
2634
+
2635
+ def join_of_Vrep(self, *indices):
2636
+ r"""
2637
+ Return the smallest face containing all Vrepresentatives indicated by the indices.
2638
+
2639
+ .. SEEALSO::
2640
+
2641
+ :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.join_of_Vrep`.
2642
+
2643
+ EXAMPLES::
2644
+
2645
+ sage: # needs sage.combinat
2646
+ sage: P = polytopes.permutahedron(4)
2647
+ sage: C = CombinatorialPolyhedron(P)
2648
+ sage: C.join_of_Vrep(0,1)
2649
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron
2650
+ sage: C.join_of_Vrep(0,11).ambient_V_indices()
2651
+ (0, 1, 10, 11, 12, 13)
2652
+ sage: C.join_of_Vrep(8).ambient_V_indices()
2653
+ (8,)
2654
+ sage: C.join_of_Vrep().ambient_V_indices()
2655
+ ()
2656
+ """
2657
+ return self.face_generator().join_of_Vrep(*indices)
2658
+
2659
+ def meet_of_Hrep(self, *indices):
2660
+ r"""
2661
+ Return the largest face contained in all facets indicated by the indices.
2662
+
2663
+ .. SEEALSO::
2664
+
2665
+ :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_Hrep`.
2666
+
2667
+ EXAMPLES::
2668
+
2669
+ sage: # needs sage.groups sage.rings.number_field
2670
+ sage: P = polytopes.dodecahedron()
2671
+ sage: C = CombinatorialPolyhedron(P)
2672
+ sage: C.meet_of_Hrep(0)
2673
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron
2674
+ sage: C.meet_of_Hrep(0).ambient_H_indices()
2675
+ (0,)
2676
+ sage: C.meet_of_Hrep(0,1).ambient_H_indices()
2677
+ (0, 1)
2678
+ sage: C.meet_of_Hrep(0,2).ambient_H_indices()
2679
+ (0, 2)
2680
+ sage: C.meet_of_Hrep(0,2,3).ambient_H_indices()
2681
+ (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
2682
+ sage: C.meet_of_Hrep().ambient_H_indices()
2683
+ ()
2684
+ """
2685
+ return self.face_generator().meet_of_Hrep(*indices)
2686
+
2687
+ def face_generator(self, dimension=None, algorithm=None):
2688
+ r"""
2689
+ Iterator over all proper faces of specified dimension.
2690
+
2691
+ INPUT:
2692
+
2693
+ - ``dimension`` -- if specified, then iterate over only this dimension
2694
+
2695
+ - ``algorithm`` -- string (optional);
2696
+ specify whether the face generator starts with facets or vertices:
2697
+
2698
+ * ``'primal'`` -- start with the facets
2699
+ * ``'dual'`` -- start with the vertices
2700
+ * ``None`` -- choose automatically
2701
+
2702
+ OUTPUT: :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`
2703
+
2704
+ .. NOTE::
2705
+
2706
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`
2707
+ can ignore subfaces or supfaces of the current face.
2708
+
2709
+ EXAMPLES::
2710
+
2711
+ sage: # needs sage.combinat
2712
+ sage: P = polytopes.permutahedron(5)
2713
+ sage: C = CombinatorialPolyhedron(P)
2714
+ sage: it = C.face_generator(dimension=2)
2715
+ sage: face = next(it); face
2716
+ A 2-dimensional face of a 4-dimensional combinatorial polyhedron
2717
+ sage: face.ambient_Vrepresentation()
2718
+ (A vertex at (1, 3, 2, 5, 4),
2719
+ A vertex at (2, 3, 1, 5, 4),
2720
+ A vertex at (3, 1, 2, 5, 4),
2721
+ A vertex at (3, 2, 1, 5, 4),
2722
+ A vertex at (2, 1, 3, 5, 4),
2723
+ A vertex at (1, 2, 3, 5, 4))
2724
+ sage: face = next(it); face
2725
+ A 2-dimensional face of a 4-dimensional combinatorial polyhedron
2726
+ sage: face.ambient_Vrepresentation()
2727
+ (A vertex at (2, 1, 4, 5, 3),
2728
+ A vertex at (3, 2, 4, 5, 1),
2729
+ A vertex at (3, 1, 4, 5, 2),
2730
+ A vertex at (1, 3, 4, 5, 2),
2731
+ A vertex at (1, 2, 4, 5, 3),
2732
+ A vertex at (2, 3, 4, 5, 1))
2733
+ sage: face.ambient_Hrepresentation()
2734
+ (An inequality (0, 0, -1, -1, 0) x + 9 >= 0,
2735
+ An inequality (0, 0, 0, -1, 0) x + 5 >= 0,
2736
+ An equation (1, 1, 1, 1, 1) x - 15 == 0)
2737
+ sage: face.ambient_H_indices()
2738
+ (25, 29, 30)
2739
+ sage: face = next(it); face
2740
+ A 2-dimensional face of a 4-dimensional combinatorial polyhedron
2741
+ sage: face.ambient_H_indices()
2742
+ (24, 29, 30)
2743
+ sage: face.ambient_V_indices()
2744
+ (32, 89, 90, 94)
2745
+
2746
+ sage: C = CombinatorialPolyhedron([[0,1,2],[0,1,3],[0,2,3],[1,2,3]])
2747
+ sage: it = C.face_generator()
2748
+ sage: for face in it: face.ambient_Vrepresentation()
2749
+ (1, 2, 3)
2750
+ (0, 2, 3)
2751
+ (0, 1, 3)
2752
+ (0, 1, 2)
2753
+ (2, 3)
2754
+ (1, 3)
2755
+ (1, 2)
2756
+ (3,)
2757
+ (2,)
2758
+ (1,)
2759
+ (0, 3)
2760
+ (0, 2)
2761
+ (0,)
2762
+ (0, 1)
2763
+
2764
+ sage: P = Polyhedron(rays=[[1,0],[0,1]], vertices=[[1,0],[0,1]])
2765
+ sage: C = CombinatorialPolyhedron(P)
2766
+ sage: it = C.face_generator(1)
2767
+ sage: for face in it: face.ambient_Vrepresentation()
2768
+ (A vertex at (0, 1), A vertex at (1, 0))
2769
+ (A ray in the direction (1, 0), A vertex at (1, 0))
2770
+ (A ray in the direction (0, 1), A vertex at (0, 1))
2771
+
2772
+ .. SEEALSO::
2773
+
2774
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`,
2775
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`.
2776
+ """
2777
+ cdef int dual
2778
+
2779
+ dual = self._algorithm_to_dual(algorithm)
2780
+
2781
+ if dual == -1:
2782
+ # Determine the faster way, to iterate through all faces.
2783
+ if not self.is_bounded() or self.n_facets() <= self.n_Vrepresentation():
2784
+ dual = 0
2785
+ else:
2786
+ dual = 1
2787
+
2788
+ return FaceIterator(self, dual, output_dimension=dimension)
2789
+
2790
+ face_iter = face_generator
2791
+
2792
+ cdef FaceIterator _face_iter(self, bint dual, int dimension):
2793
+ r"""
2794
+ A method to obtain the FaceIterator as Cython object.
2795
+
2796
+ ``dimension`` is the ``output_dimension`` of
2797
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`.
2798
+ If ``dimension == -2`` this will indicate no ``output_dimension``.
2799
+
2800
+ See :meth:`CombinatorialPolyhedron.face_iter`
2801
+ """
2802
+ if dual and not self.is_bounded():
2803
+ raise ValueError("cannot iterate over dual of unbounded polyhedron")
2804
+ if dimension == -2:
2805
+ return FaceIterator(self, dual)
2806
+ else:
2807
+ return FaceIterator(self, dual, output_dimension=dimension)
2808
+
2809
+ def face_lattice(self):
2810
+ r"""
2811
+ Generate the face-lattice.
2812
+
2813
+ OUTPUT: :class:`~sage.combinat.posets.lattices.FiniteLatticePoset`
2814
+
2815
+ .. NOTE::
2816
+
2817
+ Use :meth:`CombinatorialPolyhedron.face_by_face_lattice_index` to get
2818
+ the face for each index.
2819
+
2820
+ .. WARNING::
2821
+
2822
+ The labeling of the face lattice might depend on architecture
2823
+ and implementation. Relabeling the face lattice with
2824
+ :meth:`CombinatorialPolyhedron.face_by_face_lattice_index` or
2825
+ the properties obtained from this face will be platform independent.
2826
+
2827
+ EXAMPLES::
2828
+
2829
+ sage: P = Polyhedron(rays=[[1,0],[0,1]])
2830
+ sage: C = CombinatorialPolyhedron(P)
2831
+ sage: C.face_lattice() # needs sage.combinat
2832
+ Finite lattice containing 5 elements
2833
+
2834
+ sage: P = Polyhedron(rays=[[1,0,0], [-1,0,0], [0,-1,0], [0,1,0]])
2835
+ sage: C = CombinatorialPolyhedron(P)
2836
+ sage: P1 = Polyhedron(rays=[[1,0], [-1,0]])
2837
+ sage: C1 = CombinatorialPolyhedron(P1)
2838
+ sage: C.face_lattice().is_isomorphic(C1.face_lattice()) # needs sage.combinat
2839
+ True
2840
+
2841
+ sage: P = polytopes.permutahedron(5)
2842
+ sage: C = CombinatorialPolyhedron(P)
2843
+ sage: C.face_lattice() # needs sage.combinat
2844
+ Finite lattice containing 542 elements
2845
+
2846
+ TESTS::
2847
+
2848
+ sage: P = polytopes.cyclic_polytope(4,10)
2849
+ sage: C = CombinatorialPolyhedron(P)
2850
+ sage: C.face_lattice().is_isomorphic(P.face_lattice()) # needs sage.combinat
2851
+ True
2852
+
2853
+ sage: P = polytopes.permutahedron(4)
2854
+ sage: C = CombinatorialPolyhedron(P)
2855
+ sage: C.face_lattice().is_isomorphic(P.face_lattice()) # needs sage.combinat
2856
+ True
2857
+ """
2858
+ from sage.combinat.posets.lattices import FiniteLatticePoset
2859
+ return FiniteLatticePoset(self.hasse_diagram())
2860
+
2861
+ @cached_method
2862
+ def hasse_diagram(self):
2863
+ r"""
2864
+ Return the Hasse diagram of ``self``.
2865
+
2866
+ This is the Hasse diagram of the poset of the faces of ``self``:
2867
+ A directed graph consisting of a vertex for each face
2868
+ and an edge for each minimal inclusion of faces.
2869
+
2870
+ .. NOTE::
2871
+
2872
+ The vertices of the Hasse diagram are given by indices.
2873
+ Use :meth:`CombinatorialPolyhedron.face_by_face_lattice_index`
2874
+ to relabel.
2875
+
2876
+ .. WARNING::
2877
+
2878
+ The indices of the Hasse diagram might depend on architecture
2879
+ and implementation. Relabeling the face lattice with
2880
+ :meth:`CombinatorialPolyhedron.face_by_face_lattice_index` or
2881
+ the properties obtained from this face will be platform independent
2882
+
2883
+ EXAMPLES::
2884
+
2885
+ sage: # needs sage.graphs sage.rings.number_field
2886
+ sage: P = polytopes.regular_polygon(4).pyramid()
2887
+ sage: C = CombinatorialPolyhedron(P)
2888
+ sage: D = C.hasse_diagram(); D
2889
+ Digraph on 20 vertices
2890
+ sage: D.average_degree()
2891
+ 21/5
2892
+ sage: D.relabel(C.face_by_face_lattice_index)
2893
+ sage: dim_0_vert = D.vertices(sort=True)[1:6]; dim_0_vert
2894
+ [A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
2895
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
2896
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
2897
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
2898
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron]
2899
+ sage: sorted(D.out_degree(vertices=dim_0_vert))
2900
+ [3, 3, 3, 3, 4]
2901
+ """
2902
+ if not self._face_lattice_incidences:
2903
+ # compute all incidences.
2904
+ self._compute_face_lattice_incidences()
2905
+ if self._face_lattice_incidences is None:
2906
+ raise TypeError("could not determine face lattice")
2907
+
2908
+ # Edges of the face-lattice/Hasse diagram.
2909
+ cdef size_t j
2910
+ cdef size_t n_incidences = self._face_lattice_incidences.length
2911
+ edges = tuple(self._face_lattice_incidences[j] for j in range(n_incidences))
2912
+
2913
+ V = tuple(smallInteger(i) for i in range(sum(self._f_vector)))
2914
+
2915
+ from sage.graphs.digraph import DiGraph
2916
+ D = DiGraph([V, edges], format='vertices_and_edges', vertex_labels=False)
2917
+ return D
2918
+
2919
+ def _face_lattice_dimension(self, index):
2920
+ r"""
2921
+ Return for each element in :meth:`CombinatorialPolyhedron.face_lattice`
2922
+ its dimension.
2923
+
2924
+ EXAMPLES::
2925
+
2926
+ sage: P = polytopes.cube()
2927
+ sage: C = CombinatorialPolyhedron(P)
2928
+ sage: F = C.face_lattice() # needs sage.combinat
2929
+ sage: def f(i):
2930
+ ....: return (i, C._face_lattice_dimension(i))
2931
+ ....:
2932
+ sage: G = F.relabel(f) # needs sage.combinat
2933
+ sage: set(G._elements) # needs sage.combinat
2934
+ {(0, -1),
2935
+ (1, 0),
2936
+ (2, 0),
2937
+ (3, 0),
2938
+ (4, 0),
2939
+ (5, 0),
2940
+ (6, 0),
2941
+ (7, 0),
2942
+ (8, 0),
2943
+ (9, 1),
2944
+ (10, 1),
2945
+ (11, 1),
2946
+ (12, 1),
2947
+ (13, 1),
2948
+ (14, 1),
2949
+ (15, 1),
2950
+ (16, 1),
2951
+ (17, 1),
2952
+ (18, 1),
2953
+ (19, 1),
2954
+ (20, 1),
2955
+ (21, 2),
2956
+ (22, 2),
2957
+ (23, 2),
2958
+ (24, 2),
2959
+ (25, 2),
2960
+ (26, 2),
2961
+ (27, 3)}
2962
+ """
2963
+ f_vector = self.f_vector()
2964
+ dim = self.dimension()
2965
+
2966
+ # Getting the dimension, by considering the following:
2967
+ # The level-set of dimension `d` will have indices `k, k+1, ..., k+n-1`,
2968
+ # where `n` is the number of faces of dimension `d` ( ``n = f_vector[d + 1]``)
2969
+ # and `k` is the number of face of dimension up to `d`, i.e.
2970
+ # ``k = sum(f_vector[:d])``.
2971
+ return max(d for d in range(dim+2) if sum(f_vector[:d]) <= index) - 1
2972
+
2973
+ def face_by_face_lattice_index(self, index):
2974
+ r"""
2975
+ Return the element of :meth:`CombinatorialPolyhedron.face_lattice` with corresponding index.
2976
+
2977
+ The element will be returned as
2978
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`.
2979
+
2980
+ EXAMPLES::
2981
+
2982
+ sage: # needs sage.combinat
2983
+ sage: P = polytopes.cube()
2984
+ sage: C = CombinatorialPolyhedron(P)
2985
+ sage: F = C.face_lattice()
2986
+ sage: F
2987
+ Finite lattice containing 28 elements
2988
+ sage: G = F.relabel(C.face_by_face_lattice_index)
2989
+ sage: G.level_sets()[0]
2990
+ [A -1-dimensional face of a 3-dimensional combinatorial polyhedron]
2991
+ sage: G.level_sets()[3]
2992
+ [A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
2993
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
2994
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
2995
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
2996
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
2997
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron]
2998
+
2999
+ sage: P = Polyhedron(rays=[[0,1], [1,0]])
3000
+ sage: C = CombinatorialPolyhedron(P)
3001
+ sage: F = C.face_lattice() # needs sage.combinat
3002
+ sage: G = F.relabel(C.face_by_face_lattice_index) # needs sage.combinat
3003
+ sage: G._elements # needs sage.combinat
3004
+ (A -1-dimensional face of a 2-dimensional combinatorial polyhedron,
3005
+ A 0-dimensional face of a 2-dimensional combinatorial polyhedron,
3006
+ A 1-dimensional face of a 2-dimensional combinatorial polyhedron,
3007
+ A 1-dimensional face of a 2-dimensional combinatorial polyhedron,
3008
+ A 2-dimensional face of a 2-dimensional combinatorial polyhedron)
3009
+
3010
+ sage: def f(i): return C.face_by_face_lattice_index(i).ambient_V_indices()
3011
+ sage: G = F.relabel(f) # needs sage.combinat
3012
+ sage: G._elements # needs sage.combinat
3013
+ ((), (0,), (0, 1), (0, 2), (0, 1, 2))
3014
+ """
3015
+ self._record_all_faces() # Initialize ``_all_faces``, if not done yet.
3016
+ dim = self._face_lattice_dimension(index) # Determine dimension to that index.
3017
+ newindex = index - sum(self._f_vector[:dim + 1]) # Index in that level-set.
3018
+
3019
+ # Let ``_all_faces`` determine Vrepresentation.
3020
+ return self._all_faces.get_face(dim, newindex)
3021
+
3022
+ def a_maximal_chain(self, Vindex=None, Hindex=None):
3023
+ r"""
3024
+ Return a maximal chain of the face lattice in increasing order
3025
+ without empty face and whole polyhedron/maximal face.
3026
+
3027
+ INPUT:
3028
+
3029
+ - ``Vindex`` -- integer (default: ``None``); prescribe the index of the vertex in the chain
3030
+ - ``Hindex`` -- integer (default: ``None``); prescribe the index of the facet in the chain
3031
+
3032
+ Each face is given as
3033
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`.
3034
+
3035
+ EXAMPLES::
3036
+
3037
+ sage: P = polytopes.cross_polytope(4)
3038
+ sage: C = P.combinatorial_polyhedron()
3039
+ sage: chain = C.a_maximal_chain(); chain
3040
+ [A 0-dimensional face of a 4-dimensional combinatorial polyhedron,
3041
+ A 1-dimensional face of a 4-dimensional combinatorial polyhedron,
3042
+ A 2-dimensional face of a 4-dimensional combinatorial polyhedron,
3043
+ A 3-dimensional face of a 4-dimensional combinatorial polyhedron]
3044
+ sage: [face.ambient_V_indices() for face in chain]
3045
+ [(7,), (6, 7), (5, 6, 7), (4, 5, 6, 7)]
3046
+
3047
+ sage: P = polytopes.hypercube(4)
3048
+ sage: C = P.combinatorial_polyhedron()
3049
+ sage: chain = C.a_maximal_chain(); chain
3050
+ [A 0-dimensional face of a 4-dimensional combinatorial polyhedron,
3051
+ A 1-dimensional face of a 4-dimensional combinatorial polyhedron,
3052
+ A 2-dimensional face of a 4-dimensional combinatorial polyhedron,
3053
+ A 3-dimensional face of a 4-dimensional combinatorial polyhedron]
3054
+ sage: [face.ambient_V_indices() for face in chain]
3055
+ [(15,), (6, 15), (5, 6, 14, 15), (0, 5, 6, 7, 8, 9, 14, 15)]
3056
+
3057
+ sage: # needs sage.combinat
3058
+ sage: P = polytopes.permutahedron(4)
3059
+ sage: C = P.combinatorial_polyhedron()
3060
+ sage: chain = C.a_maximal_chain(); chain
3061
+ [A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
3062
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
3063
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron]
3064
+ sage: [face.ambient_V_indices() for face in chain]
3065
+ [(16,), (15, 16), (8, 9, 14, 15, 16, 17)]
3066
+
3067
+ sage: P = Polyhedron(rays=[[1,0]], lines=[[0,1]])
3068
+ sage: C = P.combinatorial_polyhedron()
3069
+ sage: chain = C.a_maximal_chain()
3070
+ sage: [face.ambient_V_indices() for face in chain]
3071
+ [(0, 1)]
3072
+
3073
+ sage: P = Polyhedron(rays=[[1,0,0],[0,0,1]], lines=[[0,1,0]])
3074
+ sage: C = P.combinatorial_polyhedron()
3075
+ sage: chain = C.a_maximal_chain()
3076
+ sage: [face.ambient_V_indices() for face in chain]
3077
+ [(0, 1), (0, 1, 3)]
3078
+
3079
+ sage: P = Polyhedron(rays=[[1,0,0]], lines=[[0,1,0],[0,0,1]])
3080
+ sage: C = P.combinatorial_polyhedron()
3081
+ sage: chain = C.a_maximal_chain()
3082
+ sage: [face.ambient_V_indices() for face in chain]
3083
+ [(0, 1, 2)]
3084
+
3085
+ Specify an index for the vertex of the chain::
3086
+
3087
+ sage: P = polytopes.cube()
3088
+ sage: C = P.combinatorial_polyhedron()
3089
+ sage: [face.ambient_V_indices() for face in C.a_maximal_chain()]
3090
+ [(5,), (0, 5), (0, 3, 4, 5)]
3091
+ sage: [face.ambient_V_indices() for face in C.a_maximal_chain(Vindex=2)]
3092
+ [(2,), (2, 7), (2, 3, 4, 7)]
3093
+
3094
+ Specify an index for the facet of the chain::
3095
+
3096
+ sage: [face.ambient_H_indices() for face in C.a_maximal_chain()]
3097
+ [(3, 4, 5), (4, 5), (5,)]
3098
+ sage: [face.ambient_H_indices() for face in C.a_maximal_chain(Hindex=3)]
3099
+ [(3, 4, 5), (3, 4), (3,)]
3100
+ sage: [face.ambient_H_indices() for face in C.a_maximal_chain(Hindex=2)]
3101
+ [(2, 3, 5), (2, 3), (2,)]
3102
+
3103
+ If the specified vertex is not contained in the specified facet an error is raised::
3104
+
3105
+ sage: C.a_maximal_chain(Vindex=0, Hindex=3)
3106
+ Traceback (most recent call last):
3107
+ ...
3108
+ ValueError: the given Vindex is not compatible with the given Hindex
3109
+
3110
+ An error is raised, if the specified index does not correspond to a facet::
3111
+
3112
+ sage: C.a_maximal_chain(Hindex=40)
3113
+ Traceback (most recent call last):
3114
+ ...
3115
+ ValueError: the given Hindex does not correspond to a facet
3116
+
3117
+ An error is raised, if the specified index does not correspond to a vertex::
3118
+
3119
+ sage: C.a_maximal_chain(Vindex=40)
3120
+ Traceback (most recent call last):
3121
+ ...
3122
+ ValueError: the given Vindex does not correspond to a vertex
3123
+
3124
+ ::
3125
+
3126
+ sage: P = Polyhedron(rays=[[1,0,0],[0,0,1]], lines=[[0,1,0]])
3127
+ sage: C = P.combinatorial_polyhedron()
3128
+ sage: C.a_maximal_chain(Vindex=0)
3129
+ Traceback (most recent call last):
3130
+ ...
3131
+ ValueError: the given Vindex does not correspond to a vertex
3132
+
3133
+ ::
3134
+
3135
+ sage: P = Polyhedron(rays=[[1,0,0],[0,0,1]])
3136
+ sage: C = P.combinatorial_polyhedron()
3137
+ sage: C.a_maximal_chain(Vindex=0)
3138
+ [A 0-dimensional face of a 2-dimensional combinatorial polyhedron,
3139
+ A 1-dimensional face of a 2-dimensional combinatorial polyhedron]
3140
+ sage: C.a_maximal_chain(Vindex=1)
3141
+ Traceback (most recent call last):
3142
+ ...
3143
+ ValueError: the given Vindex does not correspond to a vertex
3144
+ """
3145
+ if self.n_facets() == 0 or self.dimension() == 0:
3146
+ return []
3147
+
3148
+ # We take a face iterator and do one depth-search.
3149
+ # Depending on whether it is dual or not,
3150
+ # the search will be from the top or bottom.
3151
+ cdef FaceIterator it = self.face_generator()
3152
+ chain = [None]*(self.dimension())
3153
+ dual = it.dual
3154
+ final_dim = 0 if not dual else self.dimension()-1
3155
+
3156
+ cdef bint found_Vindex = Vindex is None
3157
+ cdef bint found_Hindex = Hindex is None
3158
+
3159
+ # For each dimension we save the first face we see.
3160
+ # This is the face whose sub-/supfaces we visit in the next step.
3161
+ current_dim = self.dimension()
3162
+ for face in it:
3163
+ if not found_Hindex:
3164
+ if Hindex not in face.ambient_H_indices():
3165
+ continue
3166
+ if face.dimension() == self.dimension() - 1:
3167
+ found_Hindex = True
3168
+ if not found_Vindex and Vindex not in face.ambient_V_indices():
3169
+ raise ValueError("the given Vindex is not compatible with the given Hindex")
3170
+ if not found_Vindex:
3171
+ if Vindex not in face.ambient_V_indices():
3172
+ continue
3173
+ if face.dimension() == 0:
3174
+ found_Vindex = True
3175
+ if not found_Hindex and Hindex not in face.ambient_H_indices():
3176
+ raise ValueError("the given Vindex is not compatible with the given Hindex")
3177
+
3178
+ it.only_subsets()
3179
+ current_dim = face.dimension()
3180
+ chain[current_dim] = face
3181
+
3182
+ if found_Vindex is False:
3183
+ raise ValueError("the given Vindex does not correspond to a vertex")
3184
+ if found_Hindex is False:
3185
+ raise ValueError("the given Hindex does not correspond to a facet")
3186
+
3187
+ if current_dim != final_dim:
3188
+ # The polyhedron contains lines.
3189
+ # Note that the iterator was always not dual
3190
+ # in this case.
3191
+ return chain[current_dim:]
3192
+ return chain
3193
+
3194
+ def _test_a_maximal_chain(self, tester=None, **options):
3195
+ """
3196
+ Run tests on the method :meth:`.a_maximal_chain`.
3197
+
3198
+ TESTS::
3199
+
3200
+ sage: polytopes.cross_polytope(3).combinatorial_polyhedron()._test_a_maximal_chain()
3201
+ """
3202
+ if tester is None:
3203
+ tester = self._tester(**options)
3204
+
3205
+ def test_a_chain(b):
3206
+ for i in range(len(b) - 1):
3207
+ tester.assertTrue(b[i].is_subface(b[i+1]))
3208
+
3209
+ if self.is_bounded():
3210
+ b = self.a_maximal_chain()
3211
+ test_a_chain(b)
3212
+ if not self.n_vertices():
3213
+ return
3214
+
3215
+ from sage.misc.prandom import randrange
3216
+
3217
+ if self.n_vertices():
3218
+ # We obtain a chain containing a random vertex.
3219
+ i = randrange(self.n_vertices())
3220
+ b = self.a_maximal_chain(Vindex=i)
3221
+ test_a_chain(b)
3222
+ tester.assertTrue(all(i in f.ambient_V_indices() for f in b))
3223
+
3224
+ if self.n_facets():
3225
+ # We obtain a chain containing a random facet.
3226
+ i = randrange(self.n_facets())
3227
+ b = self.a_maximal_chain(Hindex=i)
3228
+ test_a_chain(b)
3229
+ tester.assertTrue(all(i in f.ambient_H_indices() for f in b))
3230
+
3231
+ # We obtain a chain containing that facet
3232
+ # and a random vertex contained in it.
3233
+ facet = self.facets(names=False)[i]
3234
+ j = facet[randrange(len(facet))]
3235
+ b = self.a_maximal_chain(Vindex=j, Hindex=i)
3236
+ test_a_chain(b)
3237
+ tester.assertTrue(all(j in f.ambient_V_indices() for f in b))
3238
+ tester.assertTrue(all(i in f.ambient_H_indices() for f in b))
3239
+
3240
+ cdef tuple Vrep(self):
3241
+ r"""
3242
+ Return the names of the Vrepresentation, if they exist. Else return ``None``.
3243
+ """
3244
+ return self._Vrep
3245
+
3246
+ cdef tuple facet_names(self):
3247
+ r"""
3248
+ Return the names Hrepresentatives, which are facets.
3249
+
3250
+ If not given, return ``None``.
3251
+ """
3252
+ return self._facet_names
3253
+
3254
+ cdef tuple equations(self):
3255
+ r"""
3256
+ Return the names of the equations.
3257
+
3258
+ If not equations are given, return ``None``.
3259
+ """
3260
+ return self._equations
3261
+
3262
+ cdef unsigned int n_Vrepresentation(self) noexcept:
3263
+ r"""
3264
+ Return the number of elements in the Vrepresentation.
3265
+ """
3266
+ return self._n_Vrepresentation
3267
+
3268
+ cdef unsigned int n_Hrepresentation(self) noexcept:
3269
+ r"""
3270
+ Return the number of elements in the Hrepresentation.
3271
+ """
3272
+ return self._n_Hrepresentation
3273
+
3274
+ def is_compact(self):
3275
+ r"""
3276
+ Return whether the polyhedron is compact.
3277
+
3278
+ EXAMPLES::
3279
+
3280
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
3281
+ sage: C.is_compact()
3282
+ False
3283
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2], [1,2]])
3284
+ sage: C.is_compact()
3285
+ True
3286
+ sage: P = polytopes.simplex()
3287
+ sage: P.combinatorial_polyhedron().is_compact()
3288
+ True
3289
+ sage: P = Polyhedron(rays=P.vertices())
3290
+ sage: P.combinatorial_polyhedron().is_compact()
3291
+ False
3292
+ """
3293
+ return self.is_bounded()
3294
+
3295
+ cdef bint is_bounded(self) noexcept:
3296
+ r"""
3297
+ Return whether the polyhedron is bounded.
3298
+ """
3299
+ return self._bounded
3300
+
3301
+ cdef ListOfFaces bitrep_facets(self):
3302
+ r"""
3303
+ Return the facets in bit representation.
3304
+ """
3305
+ return self._bitrep_facets
3306
+
3307
+ cdef ListOfFaces bitrep_Vrep(self):
3308
+ r"""
3309
+ Return the Vrepresentations in bit representation.
3310
+ """
3311
+ return self._bitrep_Vrep
3312
+
3313
+ cdef tuple far_face_tuple(self):
3314
+ r"""
3315
+ Return the far face as it was given on initialization.
3316
+ """
3317
+ return self._far_face_tuple
3318
+
3319
+ def __eq__(self, other):
3320
+ r"""
3321
+ Return whether ``self`` and ``other`` are equal.
3322
+ """
3323
+ if not isinstance(other, CombinatorialPolyhedron):
3324
+ return False
3325
+ cdef CombinatorialPolyhedron other_C = other
3326
+ return (self.n_facets() == other.n_facets()
3327
+ and self.Vrepresentation() == other.Vrepresentation()
3328
+ and self.facet_names() == other_C.facet_names()
3329
+ and self.equations() == other_C.equations()
3330
+ and self.dimension() == other.dimension()
3331
+ and self.far_face_tuple() == other_C.far_face_tuple()
3332
+ and self.incidence_matrix() == other.incidence_matrix())
3333
+
3334
+ # Methods to obtain a different combinatorial polyhedron.
3335
+
3336
+ cpdef CombinatorialPolyhedron dual(self):
3337
+ r"""
3338
+ Return the dual/polar of ``self``.
3339
+
3340
+ Only defined for bounded polyhedra.
3341
+
3342
+ .. SEEALSO::
3343
+
3344
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.polar`.
3345
+
3346
+ EXAMPLES::
3347
+
3348
+ sage: P = polytopes.cube()
3349
+ sage: C = P.combinatorial_polyhedron()
3350
+ sage: D = C.dual()
3351
+ sage: D.f_vector()
3352
+ (1, 6, 12, 8, 1)
3353
+ sage: D1 = P.polar().combinatorial_polyhedron()
3354
+ sage: D1.face_lattice().is_isomorphic(D.face_lattice()) # needs sage.combinat
3355
+ True
3356
+
3357
+ Polar is an alias to be consistent with :class:`~sage.geometry.polyhedron.base.Polyhedron_base`::
3358
+
3359
+ sage: C.polar().f_vector()
3360
+ (1, 6, 12, 8, 1)
3361
+
3362
+ For unbounded polyhedra, an error is raised::
3363
+
3364
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
3365
+ sage: C.dual()
3366
+ Traceback (most recent call last):
3367
+ ...
3368
+ ValueError: self must be bounded
3369
+ """
3370
+ if not self.is_bounded():
3371
+ raise ValueError("self must be bounded")
3372
+ cdef ListOfFaces new_facets = self.bitrep_Vrep().__copy__()
3373
+ cdef ListOfFaces new_Vrep = self.bitrep_facets().__copy__()
3374
+
3375
+ return CombinatorialPolyhedron((new_facets, new_Vrep))
3376
+
3377
+ polar = dual
3378
+
3379
+ cpdef CombinatorialPolyhedron pyramid(self, new_vertex=None, new_facet=None):
3380
+ r"""
3381
+ Return the pyramid of ``self``.
3382
+
3383
+ INPUT:
3384
+
3385
+ - ``new_vertex`` -- (optional); specify a new vertex name to set up
3386
+ the pyramid with vertex names
3387
+ - ``new_facet`` -- (optional); specify a new facet name to set up
3388
+ the pyramid with facet names
3389
+
3390
+ EXAMPLES::
3391
+
3392
+ sage: C = CombinatorialPolyhedron(((1,2,3),(1,2,4),(1,3,4),(2,3,4)))
3393
+ sage: C1 = C.pyramid()
3394
+ sage: C1.facets()
3395
+ ((0, 1, 2, 4), (0, 1, 3, 4), (0, 2, 3, 4), (1, 2, 3, 4), (0, 1, 2, 3))
3396
+
3397
+ ::
3398
+
3399
+ sage: P = polytopes.cube()
3400
+ sage: C = CombinatorialPolyhedron(P)
3401
+ sage: C1 = C.pyramid()
3402
+ sage: P1 = P.pyramid()
3403
+ sage: C2 = P1.combinatorial_polyhedron()
3404
+ sage: C2.vertex_facet_graph().is_isomorphic(C1.vertex_facet_graph()) # needs sage.combinat
3405
+ True
3406
+
3407
+ One can specify a name for the new vertex::
3408
+
3409
+ sage: P = polytopes.cyclic_polytope(4,10)
3410
+ sage: C = P.combinatorial_polyhedron()
3411
+ sage: C1 = C.pyramid(new_vertex='apex')
3412
+ sage: C1.is_pyramid(certificate=True)
3413
+ (True, 'apex')
3414
+ sage: C1.facets()[0]
3415
+ (A vertex at (0, 0, 0, 0),
3416
+ A vertex at (1, 1, 1, 1),
3417
+ A vertex at (2, 4, 8, 16),
3418
+ A vertex at (3, 9, 27, 81),
3419
+ 'apex')
3420
+
3421
+ One can specify a name for the new facets::
3422
+
3423
+ sage: # needs sage.rings.number_field
3424
+ sage: P = polytopes.regular_polygon(4)
3425
+ sage: C = P.combinatorial_polyhedron()
3426
+ sage: C1 = C.pyramid(new_facet='base')
3427
+ sage: C1.Hrepresentation()
3428
+ (An inequality (-1/2, 1/2) x + 1/2 >= 0,
3429
+ An inequality (-1/2, -1/2) x + 1/2 >= 0,
3430
+ An inequality (1/2, 0.50000000000000000?) x + 1/2 >= 0,
3431
+ An inequality (1/2, -1/2) x + 1/2 >= 0,
3432
+ 'base')
3433
+
3434
+ For unbounded polyhedra, an error is raised::
3435
+
3436
+ sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True)
3437
+ sage: C.pyramid()
3438
+ Traceback (most recent call last):
3439
+ ...
3440
+ ValueError: self must be bounded
3441
+ """
3442
+ if not self.is_bounded():
3443
+ raise ValueError("self must be bounded")
3444
+ cdef ListOfFaces new_facets = self.bitrep_facets().pyramid()
3445
+ cdef ListOfFaces new_Vrep = self.bitrep_Vrep().pyramid()
3446
+
3447
+ if new_vertex is not None:
3448
+ new_Vrep_names = self.Vrepresentation() + (new_vertex,)
3449
+ else:
3450
+ new_Vrep_names = None
3451
+
3452
+ if new_facet is not None:
3453
+ if self.facet_names() is not None:
3454
+ new_facet_names = self.facet_names() + (new_facet,)
3455
+ else:
3456
+ # Closures inside cpdef functions not yet supported
3457
+ new_facet_names = self.Hrepresentation()[:self.n_facets()] + (new_facet,)
3458
+ else:
3459
+ new_facet_names = None
3460
+
3461
+ return CombinatorialPolyhedron((new_facets, new_Vrep), Vrep=new_Vrep_names, facets=new_facet_names)
3462
+
3463
+ # Internal methods.
3464
+
3465
+ cdef int _compute_f_vector(self, size_t num_threads, size_t parallelization_depth, int dual) except -1:
3466
+ r"""
3467
+ Compute the ``f_vector`` of the polyhedron.
3468
+
3469
+ See :meth:`f_vector`.
3470
+ """
3471
+ if self._f_vector:
3472
+ return 0 # There is no need to recompute the f_vector.
3473
+
3474
+ cdef int dim = self.dimension()
3475
+ cdef MemoryAllocator mem = MemoryAllocator()
3476
+
3477
+ if num_threads == 0:
3478
+ # No need to complain.
3479
+ num_threads = 1
3480
+
3481
+ if parallelization_depth > dim - 1:
3482
+ # Is a very bad choice anyway, but prevent segmentation faults.
3483
+ parallelization_depth = dim - 1
3484
+
3485
+ if dual == -1:
3486
+ if not self.is_bounded() or self.n_facets() <= self.n_Vrepresentation():
3487
+ # In this case the non-dual approach is faster.
3488
+ dual = 0
3489
+ else:
3490
+ # In this case the dual approach is faster.
3491
+ dual = 1
3492
+
3493
+ cdef FaceIterator face_iter
3494
+ cdef iter_t* structs = <iter_t*> mem.allocarray(num_threads, sizeof(iter_t))
3495
+ cdef size_t i
3496
+
3497
+ # For each thread an independent structure.
3498
+ face_iters = [self._face_iter(dual, -2) for _ in range(num_threads)]
3499
+ for i in range(num_threads):
3500
+ face_iter = face_iters[i]
3501
+ structs[i][0] = face_iter.structure[0]
3502
+
3503
+ # Initialize ``f_vector``.
3504
+ cdef size_t *f_vector = <size_t *> mem.calloc((dim + 2), sizeof(size_t))
3505
+
3506
+ parallel_f_vector(structs, num_threads, parallelization_depth, f_vector)
3507
+
3508
+ self._persist_f_vector(f_vector, dual)
3509
+
3510
+ cdef int _persist_f_vector(self, size_t* input_f_vector, bint input_is_reversed) except -1:
3511
+ cdef int dim = self.dimension()
3512
+
3513
+ if input_is_reversed:
3514
+ f_vector = \
3515
+ tuple(smallInteger(input_f_vector[dim + 1 - i]) for i in range(dim + 2))
3516
+ else:
3517
+ f_vector = \
3518
+ tuple(smallInteger(input_f_vector[i]) for i in range(dim + 2))
3519
+
3520
+ # Sanity checks.
3521
+ if dim > 1:
3522
+ if f_vector[-2] < self.n_facets():
3523
+ raise ValueError("not all facets are joins of vertices")
3524
+ if self.is_bounded() and f_vector[1] < self.n_Vrepresentation():
3525
+ raise ValueError("not all vertices are intersections of facets")
3526
+
3527
+ self._f_vector = f_vector
3528
+
3529
+ cdef int _compute_edges_or_ridges(self, int dual, bint do_edges) except -1:
3530
+ r"""
3531
+ Compute the edges of the polyhedron if ``edges`` else the ridges.
3532
+
3533
+ If ``dual``, use the face iterator in dual mode, else in non-dual.
3534
+ If ``dual`` is ``-1`` determine this automatically.
3535
+
3536
+ If the ``f_vector`` is unknown computes it as well if
3537
+ computing the edges in non-dual mode or the ridges in
3538
+ dual-mode.
3539
+
3540
+ See :meth:`CombinatorialPolyhedron.edges` and :meth:`CombinatorialPolyhedron.ridges`.
3541
+ """
3542
+ if (self._edges is not None and do_edges) or (self._ridges is not None and not do_edges):
3543
+ return 0 # There is no need to recompute.
3544
+
3545
+ if dual == -1:
3546
+ # Determine whether to use dual mode or not.
3547
+ if not self.is_bounded():
3548
+ dual = 0
3549
+ else:
3550
+ algorithm = self.choose_algorithm_to_compute_edges_or_ridges("edges" if do_edges else "ridges")
3551
+ dual = self._algorithm_to_dual(algorithm)
3552
+
3553
+ cdef FaceIterator face_iter
3554
+ cdef int dim = self.dimension()
3555
+
3556
+ cdef ListOfPairs edges = ListOfPairs()
3557
+ cdef int output_dim_init = 1 if do_edges else dim - 2
3558
+
3559
+ cdef size_t* f_vector = NULL
3560
+
3561
+ try:
3562
+ if dim == 1 and (do_edges or self.n_facets() > 1):
3563
+ # In this case there is an edge/ridge, but its not a proper face.
3564
+ edges.add(0, 1)
3565
+
3566
+ elif dim <= 1 or self.n_facets() == 0:
3567
+ # There is no edge/ridge.
3568
+ # Prevent an error when calling the face iterator.
3569
+ pass
3570
+
3571
+ else:
3572
+ if not self._f_vector and ((dual ^ do_edges)):
3573
+ # While doing edges in non-dual mode or ridges in dual-mode
3574
+ # one might as well do the f-vector.
3575
+ f_vector = <size_t *> check_calloc((dim + 2), sizeof(size_t))
3576
+ f_vector[0] = 1
3577
+ f_vector[dim + 1] = 1
3578
+ face_iter = self._face_iter(dual, -2)
3579
+ else:
3580
+ face_iter = self._face_iter(dual, output_dim_init)
3581
+ self._compute_edges_or_ridges_with_iterator(face_iter, (dual ^ do_edges),
3582
+ edges, f_vector)
3583
+
3584
+ # Success, persist the data.
3585
+ if f_vector is not NULL:
3586
+ self._persist_f_vector(f_vector, dual)
3587
+
3588
+ if do_edges:
3589
+ self._edges = edges
3590
+ else:
3591
+ self._ridges = edges
3592
+ finally:
3593
+ sig_free(f_vector)
3594
+
3595
+ if do_edges and self._edges is None:
3596
+ raise ValueError('could not determine edges')
3597
+ elif not do_edges and self._ridges is None:
3598
+ raise ValueError('could not determine ridges')
3599
+
3600
+ def choose_algorithm_to_compute_edges_or_ridges(self, edges_or_ridges):
3601
+ """
3602
+ Use some heuristics to pick primal or dual algorithm for
3603
+ computation of edges resp. ridges.
3604
+
3605
+ We estimate how long it takes to compute a face using the primal
3606
+ and the dual algorithm. This may differ significantly, so that e.g.
3607
+ visiting all faces with the primal algorithm is faster than using
3608
+ the dual algorithm to just visit vertices and edges.
3609
+
3610
+ We guess the number of edges and ridges and do a wild estimate on
3611
+ the total number of faces.
3612
+
3613
+ INPUT:
3614
+
3615
+ - ``edges_or_ridges`` -- string; one of:
3616
+ * ``'edges'``
3617
+ * ``'ridges'``
3618
+
3619
+ OUTPUT: either ``'primal'`` or ``'dual'``
3620
+
3621
+ EXAMPLES::
3622
+
3623
+ sage: C = polytopes.permutahedron(5).combinatorial_polyhedron()
3624
+ sage: C.choose_algorithm_to_compute_edges_or_ridges("edges")
3625
+ 'primal'
3626
+ sage: C.choose_algorithm_to_compute_edges_or_ridges("ridges")
3627
+ 'primal'
3628
+
3629
+ ::
3630
+
3631
+ sage: C = polytopes.cross_polytope(5).combinatorial_polyhedron()
3632
+ sage: C.choose_algorithm_to_compute_edges_or_ridges("edges")
3633
+ 'dual'
3634
+ sage: C.choose_algorithm_to_compute_edges_or_ridges("ridges")
3635
+ 'dual'
3636
+
3637
+
3638
+ ::
3639
+
3640
+ sage: C = polytopes.Birkhoff_polytope(5).combinatorial_polyhedron()
3641
+ sage: C.choose_algorithm_to_compute_edges_or_ridges("edges")
3642
+ 'dual'
3643
+ sage: C.choose_algorithm_to_compute_edges_or_ridges("ridges")
3644
+ 'primal'
3645
+ sage: C.choose_algorithm_to_compute_edges_or_ridges("something_else")
3646
+ Traceback (most recent call last):
3647
+ ...
3648
+ ValueError: unknown computation goal something_else
3649
+ """
3650
+ if self.is_simple():
3651
+ per_face_primal = self.n_Vrepresentation() * self.n_facets()
3652
+ else:
3653
+ per_face_primal = self.n_Vrepresentation() * self.n_facets() ** 2
3654
+
3655
+ if self.is_simplicial():
3656
+ per_face_dual = self.n_Vrepresentation() * self.n_facets()
3657
+ else:
3658
+ per_face_dual = self.n_Vrepresentation() ** 2 * self.n_facets()
3659
+
3660
+ from sage.arith.misc import binomial
3661
+ estimate_n_faces = self.dimension() * binomial(min(self.n_facets(), self.n_Vrepresentation()),
3662
+ self.dimension() // 2)
3663
+
3664
+ # Note that the runtime per face already computes the coatoms of the next level, i.e.
3665
+ # the runtime for each facet suffices to compute all ridges in primal,
3666
+ # the runtime for each vertex suffices to compute all edges in dual.
3667
+ if edges_or_ridges == "edges":
3668
+ estimate_primal = estimate_n_faces * per_face_primal
3669
+ estimate_dual = self.n_Vrepresentation() * per_face_dual
3670
+ elif edges_or_ridges == "ridges":
3671
+ estimate_primal = self.n_facets() * per_face_primal
3672
+ estimate_dual = estimate_n_faces * per_face_dual
3673
+ else:
3674
+ raise ValueError(f"unknown computation goal {edges_or_ridges}")
3675
+
3676
+ return 'dual' if (estimate_dual < estimate_primal) else 'primal'
3677
+
3678
+ cdef size_t _compute_edges_or_ridges_with_iterator(
3679
+ self, FaceIterator face_iter, const bint do_atom_rep,
3680
+ ListOfPairs edges, size_t* f_vector) except -1:
3681
+ r"""
3682
+ See :meth:`CombinatorialPolyhedron._compute_edges`.
3683
+ """
3684
+ cdef size_t a, b # facets of an edge
3685
+ cdef int dim = self.dimension()
3686
+ cdef bint do_f_vector = f_vector is not NULL
3687
+
3688
+ # The dimension in which to record the edges or ridges.
3689
+ cdef output_dimension = 1 if do_atom_rep else dim - 2
3690
+
3691
+ cdef int d = face_iter.next_dimension()
3692
+ while d < dim:
3693
+ sig_check()
3694
+ if do_f_vector:
3695
+ f_vector[d + 1] += 1
3696
+
3697
+ # If ``not do_f_vector`` the iterator is set up
3698
+ # for ``output_dimension`` and
3699
+ # ``d < dim`` implies
3700
+ # ``d == output_dimension``.
3701
+ if not do_f_vector or d == output_dimension:
3702
+ if do_atom_rep:
3703
+ # Set up face_iter.atom_rep
3704
+ face_iter.set_atom_rep()
3705
+
3706
+ # Copy the information.
3707
+ a = face_iter.structure.atom_rep[0]
3708
+ b = face_iter.structure.atom_rep[1]
3709
+ else:
3710
+ # Set up face_iter.coatom_rep
3711
+ face_iter.set_coatom_rep()
3712
+
3713
+ # Copy the information.
3714
+ a = face_iter.structure.coatom_rep[0]
3715
+ b = face_iter.structure.coatom_rep[1]
3716
+ edges.add(a, b)
3717
+ d = face_iter.next_dimension()
3718
+
3719
+ cdef int _compute_face_lattice_incidences(self) except -1:
3720
+ r"""
3721
+ Compute all incidences for the face lattice.
3722
+
3723
+ See :meth:`face_lattice`.
3724
+ """
3725
+ if self._face_lattice_incidences:
3726
+ return 1 # There is no need to recompute the incidences.
3727
+
3728
+ cdef int dim = self.dimension()
3729
+ f_vector = self.f_vector()
3730
+ self._record_all_faces() # set up ``self._all_faces``
3731
+ cdef PolyhedronFaceLattice all_faces = self._all_faces
3732
+
3733
+ # ``all_faces`` will store its incidences in ``first`` and ``second``.
3734
+ cdef size_t first = 0, second = 0
3735
+
3736
+ # ``dimension_one`` and ``dimension_two`` will be the dimensions of the
3737
+ # incidences, we currently obtain from ``all_faces``.
3738
+ # Almost always ``dimension_two = dimension_one - 1``.
3739
+ cdef int dimension_one, dimension_two
3740
+ cdef int j # an index for ``range(dimension_two + 1)``
3741
+
3742
+ # The indices of the incidences in ``all_faces`` are levelwise.
3743
+ # Hence, we have to add to each index dependent on dimension:
3744
+
3745
+ # For ``dimension_two`` we add:
3746
+ cdef size_t already_seen # = sum(f_vector[j] for j in range(dimension_two + 1))
3747
+
3748
+ # For ``dimension_one`` we add:
3749
+ cdef size_t already_seen_next # = sum(f_vector[j] for j in range(dimension_two + 2))
3750
+
3751
+ cdef ListOfPairs incidences = ListOfPairs()
3752
+
3753
+ if all_faces is None:
3754
+ raise ValueError("could not determine a list of all faces")
3755
+
3756
+ dimension_one = 0
3757
+ if dim > -1:
3758
+ while f_vector[dimension_one + 1] == 0:
3759
+ # Taking care of cases, where there might be no faces
3760
+ # of dimension 0, 1, etc (``n_lines > 0``).
3761
+ dimension_one += 1
3762
+ dimension_two = -1
3763
+
3764
+ while dimension_one < dim + 1:
3765
+ already_seen = sum(f_vector[j] for j in range(dimension_two + 1))
3766
+ already_seen_next = already_seen + f_vector[dimension_two + 1]
3767
+
3768
+ if all_faces.dual:
3769
+ # If ``dual``, then ``all_faces`` has the dimensions reversed.
3770
+ all_faces.incidence_init(dim - 1 - dimension_two, dim - 1 - dimension_one)
3771
+ else:
3772
+ all_faces.incidence_init(dimension_one, dimension_two)
3773
+
3774
+ # Get all incidences for fixed ``[dimension_one, dimension_two]``.
3775
+ while all_faces.next_incidence(&second, &first):
3776
+ if all_faces.dual:
3777
+ # If ``dual``, then ``second`` and ``first are flipped.
3778
+ second += already_seen
3779
+ first += already_seen_next
3780
+ incidences.add(second, first)
3781
+ else:
3782
+ second += already_seen_next
3783
+ first += already_seen
3784
+ incidences.add(first, second)
3785
+
3786
+ sig_check()
3787
+
3788
+ # Increase dimensions.
3789
+ dimension_one += 1
3790
+ dimension_two = dimension_one - 1
3791
+
3792
+ # Success, persist the data.
3793
+ self._face_lattice_incidences = incidences
3794
+
3795
+ def _record_all_faces(self):
3796
+ r"""
3797
+ Initialize :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.polyhedron_faces_lattice.PolyhedronFaceLattice` for the polyhedron.
3798
+
3799
+ Record and sort all faces of the polyhedron in that class.
3800
+
3801
+ EXAMPLES::
3802
+
3803
+ sage: P = polytopes.cyclic_polytope(4,10)
3804
+ sage: C = CombinatorialPolyhedron(P)
3805
+ sage: C._record_all_faces()
3806
+
3807
+ TESTS::
3808
+
3809
+ sage: # needs sage.combinat
3810
+ sage: P = polytopes.permutahedron(4)
3811
+ sage: C = CombinatorialPolyhedron(P)
3812
+ sage: it = C.face_generator()
3813
+ sage: tup = tuple((face.ambient_Vrepresentation(),
3814
+ ....: face.ambient_Hrepresentation()) for face in it)
3815
+ sage: rg = range(1,sum(C.f_vector()) - 1)
3816
+ sage: tup2 = tuple(
3817
+ ....: (C.face_by_face_lattice_index(i).ambient_Vrepresentation(),
3818
+ ....: C.face_by_face_lattice_index(i).ambient_Hrepresentation())
3819
+ ....: for i in rg)
3820
+ sage: sorted(tup) == sorted(tup2)
3821
+ True
3822
+
3823
+ sage: P = polytopes.cyclic_polytope(4,10)
3824
+ sage: C = CombinatorialPolyhedron(P)
3825
+ sage: it = C.face_generator()
3826
+ sage: tup = tuple((face.ambient_Vrepresentation(),face.ambient_Hrepresentation()) for face in it)
3827
+ sage: rg = range(1,sum(C.f_vector()) - 1)
3828
+ sage: tup2 = tuple((C.face_by_face_lattice_index(i).ambient_Vrepresentation(),
3829
+ ....: C.face_by_face_lattice_index(i).ambient_Hrepresentation()) for i in rg)
3830
+ sage: sorted(tup) == sorted(tup2)
3831
+ True
3832
+
3833
+ sage: P = Polyhedron(rays=[[1,0,0], [-1,0,0], [0,-1,0]])
3834
+ sage: C = CombinatorialPolyhedron(P)
3835
+ sage: it = C.face_generator()
3836
+ sage: tup = tuple((face.ambient_Vrepresentation(),face.ambient_Hrepresentation()) for face in it)
3837
+ sage: rg = range(1,sum(C.f_vector()) - 1)
3838
+ sage: tup2 = tuple((C.face_by_face_lattice_index(i).ambient_Vrepresentation(),
3839
+ ....: C.face_by_face_lattice_index(i).ambient_Hrepresentation()) for i in rg)
3840
+ sage: sorted(tup) == sorted(tup2)
3841
+ True
3842
+
3843
+ sage: P = Polyhedron(rays=[[1,0,0], [-1,0,0],
3844
+ ....: [0,-1,0], [0,1,0]])
3845
+ sage: C = CombinatorialPolyhedron(P)
3846
+ sage: it = C.face_generator()
3847
+ sage: tup = tuple((face.ambient_Vrepresentation(),face.ambient_Hrepresentation()) for face in it)
3848
+ sage: rg = range(1,sum(C.f_vector()) - 1)
3849
+ sage: tup2 = tuple((C.face_by_face_lattice_index(i).ambient_Vrepresentation(),
3850
+ ....: C.face_by_face_lattice_index(i).ambient_Hrepresentation()) for i in rg)
3851
+ sage: sorted(tup) == sorted(tup2)
3852
+ True
3853
+ """
3854
+ if self._all_faces:
3855
+ return # Have recorded all faces already.
3856
+
3857
+ self._all_faces = PolyhedronFaceLattice(self)
3858
+ if self._all_faces is None:
3859
+ raise RuntimeError("could not determine a list of all faces")