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,2274 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ # distutils: extra_compile_args = OPENMP_CFLAGS
3
+ # distutils: extra_link_args = OPENMP_CFLAGS
4
+ r"""
5
+ Face iterator for polyhedra
6
+
7
+ This iterator in principle works on every graded lattice, where
8
+ every interval of length two has exactly 4 elements (diamond property).
9
+
10
+ It also works on unbounded polyhedra, as those satisfy the diamond property,
11
+ except for intervals including the empty face.
12
+ A (slightly generalized) description of the algorithm can be found in [KS2019]_.
13
+
14
+ Terminology in this module:
15
+
16
+ - Coatoms -- the faces from which all others are constructed in the
17
+ face iterator. This will be facets or Vrep. In non-dual mode, faces
18
+ are constructed as intersections of the facets. In dual mode, they
19
+ are constructed theoretically as joins of vertices. The coatoms are
20
+ represented as incidences with the atoms they contain.
21
+
22
+ - Atoms -- facets or Vrep depending on application of algorithm. Atoms are
23
+ represented as incidences of coatoms they are contained in.
24
+
25
+ .. SEEALSO::
26
+
27
+ :mod:`sage.geometry.polyhedron.combinatorial_polyhedron.base`.
28
+
29
+ EXAMPLES:
30
+
31
+ Construct a face iterator::
32
+
33
+ sage: from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator \
34
+ ....: import FaceIterator
35
+ sage: P = polytopes.octahedron()
36
+ sage: C = CombinatorialPolyhedron(P)
37
+
38
+ sage: FaceIterator(C, False)
39
+ Iterator over the proper faces of a 3-dimensional combinatorial polyhedron
40
+ sage: FaceIterator(C, False, output_dimension=2)
41
+ Iterator over the 2-faces of a 3-dimensional combinatorial polyhedron
42
+
43
+ Iterator in the non-dual mode starts with facets::
44
+
45
+ sage: it = FaceIterator(C, False)
46
+ sage: [next(it) for _ in range(9)]
47
+ [A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
48
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
49
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
50
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
51
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
52
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
53
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
54
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
55
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron]
56
+
57
+ Iterator in the dual-mode starts with vertices::
58
+
59
+ sage: it = FaceIterator(C, True)
60
+ sage: [next(it) for _ in range(7)]
61
+ [A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
62
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
63
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
64
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
65
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
66
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
67
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron]
68
+
69
+ Obtain the Vrepresentation::
70
+
71
+ sage: it = FaceIterator(C, False)
72
+ sage: face = next(it)
73
+ sage: face.ambient_Vrepresentation()
74
+ (A vertex at (0, -1, 0), A vertex at (0, 0, -1), A vertex at (1, 0, 0))
75
+ sage: face.n_ambient_Vrepresentation()
76
+ 3
77
+
78
+ Obtain the facet-representation::
79
+
80
+ sage: it = FaceIterator(C, True)
81
+ sage: face = next(it)
82
+ sage: face.ambient_Hrepresentation()
83
+ (An inequality (-1, -1, 1) x + 1 >= 0,
84
+ An inequality (-1, -1, -1) x + 1 >= 0,
85
+ An inequality (-1, 1, -1) x + 1 >= 0,
86
+ An inequality (-1, 1, 1) x + 1 >= 0)
87
+ sage: face.ambient_H_indices()
88
+ (4, 5, 6, 7)
89
+ sage: face.n_ambient_Hrepresentation()
90
+ 4
91
+
92
+ In non-dual mode one can ignore all faces contained in the current face::
93
+
94
+ sage: it = FaceIterator(C, False)
95
+ sage: face = next(it)
96
+ sage: face.ambient_H_indices()
97
+ (7,)
98
+ sage: it.ignore_subfaces()
99
+ sage: [face.ambient_H_indices() for face in it]
100
+ [(6,),
101
+ (5,),
102
+ (4,),
103
+ (3,),
104
+ (2,),
105
+ (1,),
106
+ (0,),
107
+ (5, 6),
108
+ (1, 6),
109
+ (0, 1, 5, 6),
110
+ (4, 5),
111
+ (0, 5),
112
+ (0, 3, 4, 5),
113
+ (3, 4),
114
+ (2, 3),
115
+ (0, 3),
116
+ (0, 1, 2, 3),
117
+ (1, 2),
118
+ (0, 1)]
119
+
120
+ In dual mode one can ignore all faces that contain the current face::
121
+
122
+ sage: it = FaceIterator(C, True)
123
+ sage: face = next(it)
124
+ sage: face.ambient_V_indices()
125
+ (5,)
126
+ sage: it.ignore_supfaces()
127
+ sage: [face.ambient_V_indices() for face in it]
128
+ [(4,),
129
+ (3,),
130
+ (2,),
131
+ (1,),
132
+ (0,),
133
+ (3, 4),
134
+ (2, 4),
135
+ (0, 4),
136
+ (0, 3, 4),
137
+ (0, 2, 4),
138
+ (1, 3),
139
+ (0, 3),
140
+ (0, 1, 3),
141
+ (1, 2),
142
+ (0, 2),
143
+ (0, 1, 2),
144
+ (0, 1)]
145
+
146
+ There is a special face iterator class for geometric polyhedra.
147
+ It yields (geometric) polyhedral faces and it also yields trivial faces.
148
+ Otherwise, it works exactly the same::
149
+
150
+ sage: from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator \
151
+ ....: import FaceIterator_geom
152
+ sage: P = polytopes.cube()
153
+ sage: it = FaceIterator_geom(P)
154
+ sage: [next(it) for _ in range(5)]
155
+ [A 3-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 8 vertices,
156
+ A -1-dimensional face of a Polyhedron in ZZ^3,
157
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices,
158
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices,
159
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices]
160
+ sage: it
161
+ Iterator over the faces of a 3-dimensional polyhedron in ZZ^3
162
+
163
+ AUTHOR:
164
+
165
+ - Jonathan Kliem (2019-04)
166
+ """
167
+
168
+ # ****************************************************************************
169
+ # Copyright (C) 2019 Jonathan Kliem <jonathan.kliem@gmail.com>
170
+ #
171
+ # This program is free software: you can redistribute it and/or modify
172
+ # it under the terms of the GNU General Public License as published by
173
+ # the Free Software Foundation, either version 2 of the License, or
174
+ # (at your option) any later version.
175
+ # https://www.gnu.org/licenses/
176
+ # ****************************************************************************
177
+
178
+ from cython.parallel cimport prange, threadid
179
+ from cysignals.memory cimport check_allocarray, sig_free
180
+ from cysignals.signals cimport sig_check
181
+ from memory_allocator cimport MemoryAllocator
182
+
183
+ from sage.misc.lazy_import import LazyImport
184
+
185
+ from sage.geometry.polyhedron.combinatorial_polyhedron.base cimport CombinatorialPolyhedron
186
+ from sage.geometry.polyhedron.combinatorial_polyhedron.conversions cimport bit_rep_to_Vrep_list
187
+ from sage.geometry.polyhedron.combinatorial_polyhedron.face_list_data_structure cimport *
188
+
189
+ combinatorial_face_to_polyhedral_face = LazyImport('sage.geometry.polyhedron.face', 'combinatorial_face_to_polyhedral_face')
190
+ PolyhedronFace = LazyImport('sage.geometry.polyhedron.face', 'PolyhedronFace')
191
+
192
+
193
+ cdef extern from "Python.h":
194
+ int unlikely(int) nogil # Defined by Cython
195
+
196
+
197
+ cdef class FaceIterator_base(SageObject):
198
+ r"""
199
+ A base class to iterate over all faces of a polyhedron.
200
+
201
+ Construct all proper faces from the facets. In dual mode, construct all proper
202
+ faces from the vertices. Dual will be faster for less vertices than facets.
203
+
204
+ See :class:`FaceIterator`.
205
+ """
206
+ def __cinit__(self, P, dual=None, output_dimension=None):
207
+ r"""
208
+ Initialize :class:`FaceIterator_base`.
209
+
210
+ See :class:`FaceIterator_base`.
211
+
212
+ EXAMPLES::
213
+
214
+ sage: P = polytopes.permutahedron(4)
215
+ sage: C = CombinatorialPolyhedron(P)
216
+ sage: it = C.face_generator() # indirect doctest
217
+
218
+ sage: f_vector = [1, 0, 0, 0, 1]
219
+ sage: for face in it: f_vector[face.dimension()+1] += 1
220
+ sage: print ('f_vector of permutahedron(4): ', f_vector)
221
+ f_vector of permutahedron(4): [1, 24, 36, 14, 1]
222
+
223
+ sage: TestSuite(sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator).run()
224
+ """
225
+ # Note that all values are set to zero at the time ``__cinit__`` is called:
226
+ # https://cython.readthedocs.io/en/latest/src/userguide/special_methods.html#initialisation-methods
227
+ # In particular, ``__dealloc__`` will not do harm in this case.
228
+
229
+ cdef CombinatorialPolyhedron C
230
+
231
+ # Working around that __cinit__ of base and derived class must be the same,
232
+ # as extension classes do not yet have __new__ in Cython 0.29.
233
+ if isinstance(P, CombinatorialPolyhedron):
234
+ C = P
235
+ else:
236
+ C = P.combinatorial_polyhedron()
237
+ if dual is None:
238
+ # Determine the (likely) faster way, to iterate through all faces.
239
+ if not P.is_compact() or P.n_facets() <= P.n_vertices():
240
+ dual = False
241
+ else:
242
+ dual = True
243
+
244
+ if output_dimension is not None and (output_dimension < 0 or output_dimension >= P.dim()):
245
+ # In those cases the output will be completely handled by :meth:`FaceIterator_geom.__next__`.
246
+ output_dimension = None
247
+
248
+ if dual and not C.is_bounded():
249
+ raise ValueError("cannot iterate over dual of unbounded Polyedron")
250
+ cdef int i
251
+
252
+ self.dual = dual
253
+ self.structure.dual = dual
254
+ self.structure.dimension = C.dimension()
255
+ self.structure.current_dimension = self.structure.dimension - 1
256
+ self.structure.highest_dimension = self.structure.dimension - 1
257
+
258
+ # We will not yield the empty face.
259
+ # If there are `n` lines, than there
260
+ # are no faces below dimension `n`.
261
+ # The dimension of the level-sets in the face lattice jumps from `n` to `-1`.
262
+ self.structure.lowest_dimension = 0
263
+
264
+ if output_dimension is not None:
265
+ if output_dimension not in range(self.structure.dimension):
266
+ raise ValueError("``output_dimension`` must be the dimension of proper faces")
267
+ if self.dual:
268
+ # In dual mode, the dimensions are reversed.
269
+ self.structure.output_dimension = self.structure.dimension - 1 - output_dimension
270
+ else:
271
+ self.structure.output_dimension = output_dimension
272
+ self.structure.lowest_dimension = max(0, self.structure.output_dimension)
273
+ else:
274
+ self.structure.output_dimension = -2
275
+
276
+ if dual:
277
+ self.atoms = C.bitrep_facets()
278
+ self.coatoms = C.bitrep_Vrep()
279
+ else:
280
+ self.coatoms = C.bitrep_facets()
281
+ self.atoms = C.bitrep_Vrep()
282
+ self._Vrep = C.Vrep()
283
+ self._facet_names = C.facet_names()
284
+ self._n_facets = C.bitrep_facets().n_faces()
285
+ self._equations = C.equations()
286
+ if self._equations:
287
+ self._n_equations = len(self._equations)
288
+ else:
289
+ self._n_equations = 0
290
+ self._bounded = C.is_bounded()
291
+ self._far_face[0] = C._far_face[0]
292
+
293
+ self.structure.atom_rep = <size_t *> check_allocarray(self.coatoms.n_atoms(), sizeof(size_t))
294
+ self.structure.coatom_rep = <size_t *> check_allocarray(self.coatoms.n_faces(), sizeof(size_t))
295
+
296
+ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0:
297
+ # As we will only yield proper faces,
298
+ # there is nothing to yield in those cases.
299
+ # We have to discontinue initialization,
300
+ # as it assumes ``self.dimension > 0`` and ``self.n_faces > 0``.
301
+ self.structure.current_dimension = self.structure.dimension
302
+ return
303
+ # We may assume ``dimension > 0`` and ``n_faces > 0``.
304
+
305
+ # Initialize ``new_faces``.
306
+ self.structure.new_faces = <face_list_t*> check_calloc((self.structure.dimension), sizeof(face_list_t))
307
+ for i in range(self.structure.dimension):
308
+ face_list_init(self.structure.new_faces[i],
309
+ self.coatoms.n_faces(), self.coatoms.n_atoms(),
310
+ self.coatoms.n_coatoms())
311
+
312
+ face_list_copy(self.structure.new_faces[self.structure.dimension-1], self.coatoms.data)
313
+
314
+ # Initialize ``visited_all``.
315
+ self.structure.visited_all = <face_list_t*> check_calloc((self.structure.dimension), sizeof(face_list_t))
316
+ face_list_shallow_init(self.structure.visited_all[self.structure.dimension-1],
317
+ self.coatoms.n_faces(), self.coatoms.n_atoms(),
318
+ self.coatoms.n_coatoms())
319
+ self.structure.visited_all[self.structure.dimension-1].n_faces = 0
320
+
321
+ if not C.is_bounded():
322
+ # Treating the far face as if we had visited all its elements.
323
+ # Hence we will visit all intersections of facets unless contained in the far face.
324
+
325
+ # Regarding the length of ``self.visited_all``:
326
+ # The last facet will not yield any new faces thus the length of ``visited_all``
327
+ # needs to be at most ``n_facets - 1``.
328
+ # Hence it is fine to use the first entry already for the far face,
329
+ # as ``self.visited_all`` holds ``n_facets`` pointers.
330
+ add_face_shallow(self.structure.visited_all[self.structure.dimension-1], self._far_face)
331
+
332
+ # Initialize ``first_time``.
333
+ self.structure.first_time = <bint *> check_allocarray(self.structure.dimension, sizeof(bint))
334
+ self.structure.first_time[self.structure.dimension - 1] = True
335
+
336
+ self.structure.yet_to_visit = self.coatoms.n_faces()
337
+ self.structure._index = 0
338
+
339
+ self.structure.n_coatoms = self.coatoms.n_faces()
340
+
341
+ if C.is_bounded() and ((dual and C.is_simplicial()) or (not dual and C.is_simple())):
342
+ # We are in the comfortable situation that for our iterator
343
+ # all intervals not containing the 0 element are boolean.
344
+ # This makes things a lot easier.
345
+ self.structure.new_faces[self.structure.dimension -1].polyhedron_is_simple = True
346
+ else:
347
+ self.structure.new_faces[self.structure.dimension -1].polyhedron_is_simple = False
348
+
349
+ def __dealloc__(self):
350
+ """
351
+ TESTS::
352
+
353
+ sage: from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator import FaceIterator_base
354
+ sage: FaceIterator_base(2) # indirect doctest
355
+ Traceback (most recent call last):
356
+ ...
357
+ AttributeError: 'sage.rings.integer.Integer' object has no attribute 'combinatorial_polyhedron'...
358
+ """
359
+ cdef int i
360
+ sig_free(self.structure.atom_rep)
361
+ sig_free(self.structure.coatom_rep)
362
+ sig_free(self.structure.first_time)
363
+ if self.structure.visited_all:
364
+ face_list_shallow_free(self.structure.visited_all[self.structure.dimension - 1])
365
+ sig_free(self.structure.visited_all)
366
+ if self.structure.new_faces:
367
+ for i in range(self.structure.dimension):
368
+ face_list_free(self.structure.new_faces[i])
369
+ sig_free(self.structure.new_faces)
370
+
371
+ def reset(self):
372
+ r"""
373
+ Reset the iterator.
374
+
375
+ The iterator will start with the first face again.
376
+
377
+ EXAMPLES::
378
+
379
+ sage: P = polytopes.cube()
380
+ sage: C = P.combinatorial_polyhedron()
381
+ sage: it = C.face_generator()
382
+ sage: next(it).ambient_V_indices()
383
+ (0, 3, 4, 5)
384
+ sage: it.reset()
385
+ sage: next(it).ambient_V_indices()
386
+ (0, 3, 4, 5)
387
+
388
+ TESTS:
389
+
390
+ Resetting will fix the order of the coatoms after ``only_subsets``::
391
+
392
+ sage: P = polytopes.Birkhoff_polytope(3)
393
+ sage: C = P.combinatorial_polyhedron()
394
+ sage: it = C.face_generator(algorithm='primal')
395
+ sage: face = next(it)
396
+ sage: face.ambient_H_indices(add_equations=False)
397
+ (8,)
398
+ sage: face = next(it)
399
+ sage: face.ambient_H_indices(add_equations=False)
400
+ (7,)
401
+ sage: it.only_subfaces()
402
+ sage: it.reset()
403
+ sage: face = next(it)
404
+ sage: face.ambient_H_indices(add_equations=False)
405
+ (8,)
406
+ """
407
+ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0:
408
+ # As we will only yield proper faces,
409
+ # there is nothing to yield in those cases.
410
+ # We have to discontinue initialization,
411
+ # as it assumes ``self.dimension > 0`` and ``self.n_faces > 0``.
412
+ self.structure.current_dimension = self.structure.dimension
413
+ return
414
+ if self._bounded:
415
+ self.structure.visited_all[self.structure.dimension -1].n_faces = 0
416
+ else:
417
+ self.structure.visited_all[self.structure.dimension -1].n_faces = 1
418
+ self.structure.face_status = FaceStatus.NOT_INITIALIZED
419
+ self.structure.new_faces[self.structure.dimension - 1].n_faces = self.coatoms.n_faces()
420
+ self.structure.current_dimension = self.structure.dimension - 1
421
+ self.structure.highest_dimension = self.structure.dimension - 1
422
+ self.structure.first_time[self.structure.dimension - 1] = True
423
+
424
+ self.structure.yet_to_visit = self.coatoms.n_faces()
425
+ self.structure._index = 0
426
+
427
+ # ``only_subsets`` might have messed up the coatoms.
428
+ face_list_copy(self.structure.new_faces[self.structure.dimension-1], self.coatoms.data)
429
+
430
+ def __next__(self):
431
+ r"""
432
+ Must be implemented by a derived class.
433
+
434
+ TESTS::
435
+
436
+ sage: from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator \
437
+ ....: import FaceIterator_base
438
+ sage: P = polytopes.octahedron()
439
+ sage: C = CombinatorialPolyhedron(P)
440
+ sage: next(FaceIterator_base(C, False))
441
+ Traceback (most recent call last):
442
+ ...
443
+ NotImplementedError: a derived class must implement this
444
+ """
445
+ raise NotImplementedError("a derived class must implement this")
446
+
447
+ next = __next__
448
+
449
+ def current(self):
450
+ r"""
451
+ Retrieve the last value of :meth:`next`.
452
+
453
+ EXAMPLES::
454
+
455
+ sage: P = polytopes.octahedron()
456
+ sage: it = P.combinatorial_polyhedron().face_generator()
457
+ sage: next(it)
458
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron
459
+ sage: it.current()
460
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron
461
+ sage: next(it).ambient_V_indices() == it.current().ambient_V_indices()
462
+ True
463
+ """
464
+ if unlikely(self.structure.face_status == FaceStatus.NOT_INITIALIZED):
465
+ raise ValueError("iterator not set to a face yet")
466
+ return CombinatorialFace(self)
467
+
468
+ def __iter__(self):
469
+ r"""
470
+ EXAMPLES::
471
+
472
+ sage: P = polytopes.simplex()
473
+ sage: C = CombinatorialPolyhedron(P)
474
+ sage: it = C.face_generator()
475
+ sage: [d for d in it]
476
+ [A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
477
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
478
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
479
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
480
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
481
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
482
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
483
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
484
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
485
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
486
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
487
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
488
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
489
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron]
490
+ """
491
+ return self
492
+
493
+ def __reduce__(self):
494
+ r"""
495
+ Override __reduce__ to indicate that pickle/unpickle will not work.
496
+
497
+ EXAMPLES::
498
+
499
+ sage: P = polytopes.simplex()
500
+ sage: C = CombinatorialPolyhedron(P)
501
+ sage: it = C.face_generator()
502
+ sage: it1 = loads(it.dumps())
503
+ Traceback (most recent call last):
504
+ ...
505
+ NotImplementedError
506
+ """
507
+ raise NotImplementedError
508
+
509
+ def ignore_subfaces(self):
510
+ r"""
511
+ The iterator will not visit any faces of the current face.
512
+
513
+ Only possible when not in dual mode.
514
+
515
+ EXAMPLES::
516
+
517
+ sage: P = polytopes.Gosset_3_21()
518
+ sage: C = CombinatorialPolyhedron(P)
519
+ sage: it = C.face_generator(algorithm='primal')
520
+ sage: n_non_simplex_faces = 1
521
+ sage: for face in it:
522
+ ....: if face.n_ambient_Vrepresentation() > face.dimension() + 1:
523
+ ....: n_non_simplex_faces += 1
524
+ ....: else:
525
+ ....: it.ignore_subfaces()
526
+ ....:
527
+ sage: n_non_simplex_faces
528
+ 127
529
+
530
+ Face iterator must not be in dual mode::
531
+
532
+ sage: it = C.face_generator(algorithm='dual')
533
+ sage: _ = next(it)
534
+ sage: it.ignore_subfaces()
535
+ Traceback (most recent call last):
536
+ ...
537
+ ValueError: only possible when not in dual mode
538
+
539
+ Ignoring the same face as was requested to visit only consumes the iterator::
540
+
541
+ sage: it = C.face_generator(algorithm='primal')
542
+ sage: _ = next(it)
543
+ sage: it.only_subfaces()
544
+ sage: it.ignore_subfaces()
545
+ sage: list(it)
546
+ []
547
+
548
+ Face iterator must be set to a face first::
549
+
550
+ sage: it = C.face_generator(algorithm='primal')
551
+ sage: it.ignore_subfaces()
552
+ Traceback (most recent call last):
553
+ ...
554
+ ValueError: iterator not set to a face yet
555
+ """
556
+ if unlikely(self.dual):
557
+ raise ValueError("only possible when not in dual mode")
558
+ self.ignore_subsets()
559
+
560
+ def ignore_supfaces(self):
561
+ r"""
562
+ The iterator will not visit any faces containing the current face.
563
+
564
+ Only possible when in dual mode.
565
+
566
+ EXAMPLES::
567
+
568
+ sage: P = polytopes.Gosset_3_21()
569
+ sage: C = CombinatorialPolyhedron(P)
570
+ sage: it = C.face_generator(algorithm='dual')
571
+ sage: n_faces_with_non_simplex_quotient = 1
572
+ sage: for face in it:
573
+ ....: n_facets = face.n_ambient_Hrepresentation(add_equations=False)
574
+ ....: if n_facets > C.dimension() - face.dimension() + 1:
575
+ ....: n_faces_with_non_simplex_quotient += 1
576
+ ....: else:
577
+ ....: it.ignore_supfaces()
578
+ ....:
579
+ sage: n_faces_with_non_simplex_quotient
580
+ 4845
581
+
582
+ Face iterator must be in dual mode::
583
+
584
+ sage: it = C.face_generator(algorithm='primal')
585
+ sage: _ = next(it)
586
+ sage: it.ignore_supfaces()
587
+ Traceback (most recent call last):
588
+ ...
589
+ ValueError: only possible when in dual mode
590
+ """
591
+ if unlikely(not self.dual):
592
+ raise ValueError("only possible when in dual mode")
593
+ self.ignore_subsets()
594
+
595
+ def meet_of_Hrep(self, *indices):
596
+ r"""
597
+ Construct the meet of the facets indicated by the indices.
598
+
599
+ This is the largest face contained in all facets with the given indices.
600
+
601
+ The iterator must be reset if not newly initialized.
602
+
603
+ EXAMPLES::
604
+
605
+ sage: P = polytopes.cube()
606
+ sage: it = P.face_generator()
607
+ sage: it.meet_of_Hrep(1,2)
608
+ A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices
609
+ sage: it.meet_of_Hrep(1,2).ambient_H_indices()
610
+ (1, 2)
611
+ sage: it.meet_of_Hrep(1,3).ambient_H_indices()
612
+ (1, 3)
613
+ sage: it.meet_of_Hrep(1,5).ambient_H_indices()
614
+ (0, 1, 2, 3, 4, 5)
615
+
616
+ sage: P = polytopes.cross_polytope(4)
617
+ sage: it = P.face_generator()
618
+ sage: it.meet_of_Hrep().ambient_H_indices()
619
+ ()
620
+ sage: it.meet_of_Hrep(1,3).ambient_H_indices()
621
+ (1, 2, 3, 4)
622
+ sage: it.meet_of_Hrep(1,2).ambient_H_indices()
623
+ (1, 2)
624
+ sage: it.meet_of_Hrep(1,6).ambient_H_indices()
625
+ (1, 6)
626
+ sage: it.meet_of_Hrep(1,2,6).ambient_H_indices()
627
+ (1, 2, 6, 7)
628
+ sage: it.meet_of_Hrep(1,2,5,6).ambient_H_indices()
629
+ (0, 1, 2, 3, 4, 5, 6, 7)
630
+
631
+ sage: s = cones.schur(4)
632
+ sage: C = CombinatorialPolyhedron(s)
633
+ sage: it = C.face_generator()
634
+ sage: it.meet_of_Hrep(1,2).ambient_H_indices()
635
+ (1, 2)
636
+ sage: it.meet_of_Hrep(1,2,3).ambient_H_indices()
637
+ Traceback (most recent call last):
638
+ ...
639
+ IndexError: coatoms out of range
640
+
641
+ If the iterator has already been used, it must be reset before::
642
+
643
+ sage: # needs sage.groups sage.rings.number_field
644
+ sage: P = polytopes.dodecahedron()
645
+ sage: it = P.face_generator()
646
+ sage: _ = next(it), next(it)
647
+ sage: next(it).ambient_V_indices()
648
+ (15, 16, 17, 18, 19)
649
+ sage: it.meet_of_Hrep(9,11)
650
+ Traceback (most recent call last):
651
+ ...
652
+ ValueError: please reset the face iterator
653
+ sage: it.reset()
654
+ sage: it.meet_of_Hrep(9,11).ambient_H_indices()
655
+ (9, 11)
656
+
657
+ TESTS:
658
+
659
+ Check that things work fine, if the face iterator was never properly initialized::
660
+
661
+ sage: P = Polyhedron()
662
+ sage: P.meet_of_Hrep()
663
+ A -1-dimensional face of a Polyhedron in ZZ^0
664
+ sage: P = Polyhedron([[0,0]])
665
+ sage: P.meet_of_Hrep()
666
+ A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex
667
+ sage: P.meet_of_Hrep(0)
668
+ A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex
669
+ sage: P = Polyhedron(lines=[[1]])
670
+ sage: P.meet_of_Hrep()
671
+ A 1-dimensional face of a Polyhedron in ZZ^1 defined as the convex hull of 1 vertex and 1 line
672
+ sage: P = Polyhedron(lines=[[1, 1]])
673
+ sage: P.meet_of_Hrep()
674
+ A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line
675
+ sage: P.meet_of_Hrep(0)
676
+ A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line
677
+ """
678
+ # Ignore equations.
679
+ indices = [i for i in indices
680
+ if not (self._n_facets <= i < self._n_facets + self._n_equations)]
681
+ if self.dual:
682
+ return self._join_of_atoms(*indices)
683
+ else:
684
+ return self._meet_of_coatoms(*indices)
685
+
686
+ def join_of_Vrep(self, *indices):
687
+ r"""
688
+ Construct the join of the Vrepresentatives indicated by the indices.
689
+
690
+ This is the smallest face containing all Vrepresentatives with the given indices.
691
+
692
+ The iterator must be reset if not newly initialized.
693
+
694
+ .. NOTE::
695
+
696
+ In the case of unbounded polyhedra, the smallest face containing given Vrepresentatives
697
+ may not be well defined.
698
+
699
+ EXAMPLES::
700
+
701
+ sage: P = polytopes.cube()
702
+ sage: it = P.face_generator()
703
+ sage: it.join_of_Vrep(1)
704
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
705
+ sage: it.join_of_Vrep(1,2).ambient_V_indices()
706
+ (1, 2)
707
+ sage: it.join_of_Vrep(1,3).ambient_V_indices()
708
+ (0, 1, 2, 3)
709
+ sage: it.join_of_Vrep(1,5).ambient_V_indices()
710
+ (0, 1, 5, 6)
711
+
712
+ sage: P = polytopes.cross_polytope(4)
713
+ sage: it = P.face_generator()
714
+ sage: it.join_of_Vrep().ambient_V_indices()
715
+ ()
716
+ sage: it.join_of_Vrep(1,3).ambient_V_indices()
717
+ (1, 3)
718
+ sage: it.join_of_Vrep(1,2).ambient_V_indices()
719
+ (1, 2)
720
+ sage: it.join_of_Vrep(1,6).ambient_V_indices()
721
+ (0, 1, 2, 3, 4, 5, 6, 7)
722
+ sage: it.join_of_Vrep(8)
723
+ Traceback (most recent call last):
724
+ ...
725
+ IndexError: coatoms out of range
726
+
727
+ If the iterator has already been used, it must be reset before::
728
+
729
+ sage: # needs sage.groups sage.rings.number_field
730
+ sage: P = polytopes.dodecahedron()
731
+ sage: it = P.face_generator()
732
+ sage: _ = next(it), next(it)
733
+ sage: next(it).ambient_V_indices()
734
+ (15, 16, 17, 18, 19)
735
+ sage: it.join_of_Vrep(1,10)
736
+ Traceback (most recent call last):
737
+ ...
738
+ ValueError: please reset the face iterator
739
+ sage: it.reset()
740
+ sage: it.join_of_Vrep(1,10).ambient_V_indices()
741
+ (1, 10)
742
+
743
+ In the case of an unbounded polyhedron, we try to make sense of the input::
744
+
745
+ sage: P = polytopes.cube()*Polyhedron(lines=[[1]])
746
+ sage: it = P.face_generator()
747
+ sage: it.join_of_Vrep(1)
748
+ A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line
749
+ sage: it.join_of_Vrep(0, 1)
750
+ A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line
751
+ sage: it.join_of_Vrep(0)
752
+ Traceback (most recent call last):
753
+ ...
754
+ ValueError: the join is not well-defined
755
+
756
+ sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]])
757
+ sage: it = P.face_generator()
758
+ sage: it.join_of_Vrep(0)
759
+ A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex
760
+ sage: it.join_of_Vrep(1)
761
+ A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex
762
+ sage: it.join_of_Vrep(2)
763
+ Traceback (most recent call last):
764
+ ...
765
+ ValueError: the join is not well-defined
766
+ sage: it.join_of_Vrep(0,2)
767
+ A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray
768
+
769
+ sage: P = Polyhedron(rays=[[1,0], [0,1]])
770
+ sage: it = P.face_generator()
771
+ sage: it.join_of_Vrep(0)
772
+ A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex
773
+ sage: it.join_of_Vrep(1,2)
774
+ A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays
775
+
776
+ TESTS:
777
+
778
+ Check that things work fine, if the face iterator was never properly initialized::
779
+
780
+ sage: P = Polyhedron()
781
+ sage: P.join_of_Vrep()
782
+ A -1-dimensional face of a Polyhedron in ZZ^0
783
+ sage: P = Polyhedron([[0,0]])
784
+ sage: P.join_of_Vrep()
785
+ A -1-dimensional face of a Polyhedron in ZZ^2
786
+ sage: P.join_of_Vrep(0)
787
+ A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex
788
+ sage: P = Polyhedron(lines=[[1]])
789
+ sage: P.join_of_Vrep()
790
+ A -1-dimensional face of a Polyhedron in ZZ^1
791
+ sage: P.join_of_Vrep(0)
792
+ A 1-dimensional face of a Polyhedron in ZZ^1 defined as the convex hull of 1 vertex and 1 line
793
+ sage: P = Polyhedron(lines=[[1, 1]])
794
+ sage: P.join_of_Vrep()
795
+ A -1-dimensional face of a Polyhedron in ZZ^2
796
+ sage: P.Vrepresentation()
797
+ (A line in the direction (1, 1), A vertex at (0, 0))
798
+ sage: P.join_of_Vrep(0)
799
+ A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line
800
+ sage: P.join_of_Vrep(1)
801
+ A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line
802
+ sage: P = Polyhedron(lines=[[1, 0], [0, 1]])
803
+ sage: P.join_of_Vrep()
804
+ A -1-dimensional face of a Polyhedron in ZZ^2
805
+ sage: P.join_of_Vrep(0)
806
+ A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines
807
+ sage: P.join_of_Vrep(0, 1)
808
+ A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines
809
+ sage: P.join_of_Vrep(0, 1, 2)
810
+ A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines
811
+ """
812
+ if not self.dual:
813
+ return self._join_of_atoms(*indices)
814
+ else:
815
+ return self._meet_of_coatoms(*indices)
816
+
817
+ def _meet_of_coatoms(self, *indices):
818
+ r"""
819
+ Construct the meet of the coatoms indicated by the indices.
820
+
821
+ The iterator must be reset if not newly initialized.
822
+
823
+ .. SEEALSO::
824
+
825
+ :meth:`meet_of_Hrep`,
826
+ :meth:`join_of_Vrep`.
827
+
828
+ EXAMPLES:
829
+
830
+ In non-dual mode we construct the meet of facets::
831
+
832
+ sage: P = polytopes.cube()
833
+ sage: it = P.face_generator(algorithm='primal')
834
+ sage: it._meet_of_coatoms(1,2)
835
+ A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices
836
+ sage: it._meet_of_coatoms(1,2,3)
837
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
838
+ sage: it._meet_of_coatoms(1,2,3).ambient_H_indices()
839
+ (1, 2, 3)
840
+
841
+ In dual mode we construct the join of vertices/rays::
842
+
843
+ sage: P = polytopes.cube()
844
+ sage: it = P.face_generator(algorithm='dual')
845
+ sage: it._meet_of_coatoms(1,2)
846
+ A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices
847
+ sage: it._meet_of_coatoms(1,2,3)
848
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices
849
+ sage: it._meet_of_coatoms(1)
850
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
851
+
852
+ The face iterator must not have the output dimension specified::
853
+
854
+ sage: # needs sage.groups sage.rings.number_field
855
+ sage: P = polytopes.dodecahedron()
856
+ sage: it = P.face_generator(2)
857
+ sage: it._meet_of_coatoms(1,2)
858
+ Traceback (most recent call last):
859
+ ...
860
+ ValueError: face iterator must not have the output dimension specified
861
+
862
+ TESTS:
863
+
864
+ We prevent a segmentation fault::
865
+
866
+ sage: P = polytopes.simplex()
867
+ sage: it = P.face_generator()
868
+ sage: it._meet_of_coatoms(-1)
869
+ Traceback (most recent call last):
870
+ ...
871
+ IndexError: coatoms out of range
872
+ sage: it._meet_of_coatoms(100)
873
+ Traceback (most recent call last):
874
+ ...
875
+ IndexError: coatoms out of range
876
+
877
+ The empty face is detected correctly, even with lines or rays::
878
+
879
+ sage: P = polytopes.cube()*Polyhedron(lines=[[1]])
880
+ sage: it = P.face_generator()
881
+ sage: it._meet_of_coatoms(1,2,4,5)
882
+ A -1-dimensional face of a Polyhedron in ZZ^4
883
+
884
+ sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]])
885
+ sage: it = P.face_generator()
886
+ sage: it._meet_of_coatoms(0)
887
+ A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices
888
+ sage: it._meet_of_coatoms(1)
889
+ A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray
890
+ sage: it._meet_of_coatoms(2)
891
+ A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray
892
+ sage: it._meet_of_coatoms(1, 2)
893
+ A -1-dimensional face of a Polyhedron in QQ^2
894
+ """
895
+ if unlikely(self.structure.face_status != FaceStatus.NOT_INITIALIZED):
896
+ raise ValueError("please reset the face iterator")
897
+ if unlikely(self.structure.output_dimension != -2):
898
+ raise ValueError("face iterator must not have the output dimension specified")
899
+
900
+ cdef size_t n_atoms = self.coatoms.n_atoms()
901
+ cdef size_t n_coatoms = self.coatoms.n_faces()
902
+ cdef ListOfFaces coatoms = self.coatoms
903
+
904
+ cdef ListOfFaces face_mem = ListOfFaces(1, n_atoms, n_coatoms)
905
+ cdef face_t face = face_mem.data.faces[0]
906
+ cdef int i
907
+ cdef size_t j
908
+
909
+ # Initialize the full polyhedron.
910
+ for j in range(n_atoms):
911
+ face_add_atom(face, j)
912
+
913
+ for i in indices:
914
+ if not 0 <= i < n_coatoms:
915
+ raise IndexError("coatoms out of range")
916
+ face_intersection(face, face, coatoms.data.faces[i])
917
+
918
+ if not self._bounded and face_issubset(face, self._far_face):
919
+ # The meet is contained in the far face and therefore is the empty face.
920
+ face_clear(face)
921
+
922
+ self.find_face(face)
923
+ output = self.current()
924
+ self.reset()
925
+ return output
926
+
927
+ def _join_of_atoms(self, *indices):
928
+ r"""
929
+ Construct the join of atoms indicated by the indices.
930
+
931
+ The iterator must be reset if not newly initialized.
932
+
933
+ .. SEEALSO::
934
+
935
+ :meth:`meet_of_Hrep`,
936
+ :meth:`join_of_Vrep`.
937
+
938
+ EXAMPLES:
939
+
940
+ In dual mode we construct the meet of facets::
941
+
942
+ sage: P = polytopes.cube()
943
+ sage: it = P.face_generator(algorithm='dual')
944
+ sage: it._join_of_atoms(1,2)
945
+ A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices
946
+ sage: it._join_of_atoms(1,2,3)
947
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
948
+ sage: it._join_of_atoms(1,2,3).ambient_H_indices()
949
+ (1, 2, 3)
950
+
951
+ In non-dual mode we construct the join of vertices/rays::
952
+
953
+ sage: P = polytopes.cube()
954
+ sage: it = P.face_generator(algorithm='primal')
955
+ sage: it._join_of_atoms(1,2)
956
+ A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices
957
+ sage: it._join_of_atoms(1,2,3)
958
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices
959
+ sage: it._join_of_atoms(1)
960
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
961
+
962
+ If the iterator has already been used, it must be reset before::
963
+
964
+ sage: # needs sage.groups sage.rings.number_field
965
+ sage: P = polytopes.dodecahedron()
966
+ sage: it = P.face_generator()
967
+ sage: _ = next(it), next(it)
968
+ sage: next(it).ambient_V_indices()
969
+ (15, 16, 17, 18, 19)
970
+ sage: it._join_of_atoms(1,10)
971
+ Traceback (most recent call last):
972
+ ...
973
+ ValueError: please reset the face iterator
974
+ sage: it.reset()
975
+ sage: it._join_of_atoms(1,10).ambient_V_indices()
976
+ (1, 10)
977
+
978
+ The face iterator must not have the output dimension specified::
979
+
980
+ sage: # needs sage.groups sage.rings.number_field
981
+ sage: P = polytopes.dodecahedron()
982
+ sage: it = P.face_generator(2)
983
+ sage: it._join_of_atoms(1,2)
984
+ Traceback (most recent call last):
985
+ ...
986
+ ValueError: face iterator must not have the output dimension specified
987
+
988
+ TESTS:
989
+
990
+ We prevent a segmentation fault::
991
+
992
+ sage: P = polytopes.simplex()
993
+ sage: it = P.face_generator()
994
+ sage: it._join_of_atoms(-1)
995
+ Traceback (most recent call last):
996
+ ...
997
+ IndexError: atoms out of range
998
+ sage: it._join_of_atoms(100)
999
+ Traceback (most recent call last):
1000
+ ...
1001
+ IndexError: atoms out of range
1002
+ """
1003
+ if unlikely(self.structure.face_status != FaceStatus.NOT_INITIALIZED):
1004
+ raise ValueError("please reset the face iterator")
1005
+ if unlikely(self.structure.output_dimension != -2):
1006
+ raise ValueError("face iterator must not have the output dimension specified")
1007
+
1008
+ cdef size_t n_atoms = self.coatoms.n_atoms()
1009
+ cdef size_t n_coatoms = self.coatoms.n_faces()
1010
+ cdef ListOfFaces coatoms = self.coatoms
1011
+
1012
+ cdef ListOfFaces face_mem = ListOfFaces(2, n_atoms, n_coatoms)
1013
+ cdef face_t face = face_mem.data.faces[0]
1014
+ cdef face_t pseudo_face = face_mem.data.faces[1]
1015
+ cdef int j
1016
+ cdef size_t i
1017
+
1018
+ if not all(0 <= j < n_atoms for j in indices):
1019
+ raise IndexError("atoms out of range")
1020
+
1021
+ # Initialize a pseudo_face as indicated by the indices.
1022
+ for i in indices:
1023
+ face_add_atom(pseudo_face, i)
1024
+
1025
+ # Initialize the full polyhedron.
1026
+ for i in range(n_atoms):
1027
+ face_add_atom(face, i)
1028
+
1029
+ # Now we intersect all faces that contain our pseudo_face.
1030
+ for i in range(n_coatoms):
1031
+ if face_issubset(pseudo_face, coatoms.data.faces[i]):
1032
+ face_intersection(face, face, coatoms.data.faces[i])
1033
+
1034
+ if not indices:
1035
+ # The neutral element of the join.
1036
+ face_clear(face)
1037
+ elif not self._bounded and face_issubset(face, self._far_face):
1038
+ # The join is not well-defined.
1039
+ # We allow for unbounded polyhedra to compute the join,
1040
+ # even with rays.
1041
+ # However, the result is not necessarily well-defined.
1042
+ raise ValueError("the join is not well-defined")
1043
+
1044
+ self.find_face(face)
1045
+ output = self.current()
1046
+ self.reset()
1047
+ return output
1048
+
1049
+ cdef int ignore_subsets(self) except -1:
1050
+ r"""
1051
+ Ignore sub-/supfaces of the current face.
1052
+
1053
+ In non-dual mode ignores all subfaces of the current face.
1054
+ In dual mode ignores all supfaces of the current face.
1055
+
1056
+ See :meth:`FaceIterator_base.ignore_subfaces` and
1057
+ :meth:`FaceIterator_base.ignore_supfaces`.
1058
+ """
1059
+ if unlikely(self.structure.face_status == FaceStatus.NOT_INITIALIZED):
1060
+ raise ValueError("iterator not set to a face yet")
1061
+ if unlikely(self.structure.face_status == FaceStatus.ONLY_VISIT_SUBSETS):
1062
+ # The iterator is consumed, if it was just set to visit only subsets
1063
+ # next thing to ignore subsets.
1064
+ self.structure.current_dimension = self.structure.dimension
1065
+ return 0
1066
+ if unlikely(self.structure.face_status == FaceStatus.IGNORE_SUBSETS):
1067
+ # Nothing to do.
1068
+ return 0
1069
+ # The current face is added to ``visited_all``.
1070
+ # This will make the iterator skip those faces.
1071
+ # Also, this face will not be added a second time to ``visited_all``,
1072
+ # as there are no new faces.
1073
+
1074
+ add_face_shallow(self.structure.visited_all[self.structure.current_dimension], self.structure.face)
1075
+ self.structure.face_status = FaceStatus.IGNORE_SUBSETS
1076
+
1077
+ def only_subfaces(self):
1078
+ r"""
1079
+ The iterator will visit all (remaining) subfaces of the current face and then terminate.
1080
+
1081
+ EXAMPLES::
1082
+
1083
+ sage: P = polytopes.cube()
1084
+ sage: it = P.face_generator()
1085
+ sage: next(it).ambient_H_indices()
1086
+ ()
1087
+ sage: next(it).ambient_H_indices()
1088
+ (0, 1, 2, 3, 4, 5)
1089
+ sage: next(it).ambient_H_indices()
1090
+ (5,)
1091
+ sage: next(it).ambient_H_indices()
1092
+ (4,)
1093
+ sage: it.only_subfaces()
1094
+ sage: list(f.ambient_H_indices() for f in it)
1095
+ [(4, 5), (3, 4), (1, 4), (0, 4), (3, 4, 5), (0, 4, 5), (1, 3, 4), (0, 1, 4)]
1096
+
1097
+ ::
1098
+
1099
+ sage: P = polytopes.Birkhoff_polytope(4)
1100
+ sage: C = P.combinatorial_polyhedron()
1101
+ sage: it = C.face_generator()
1102
+ sage: next(it).ambient_H_indices(add_equations=False)
1103
+ (15,)
1104
+ sage: next(it).ambient_H_indices(add_equations=False)
1105
+ (14,)
1106
+ sage: it.only_subfaces()
1107
+ sage: all(14 in f.ambient_H_indices() for f in it)
1108
+ True
1109
+
1110
+ Face iterator needs to be set to a face first::
1111
+
1112
+ sage: it = C.face_generator()
1113
+ sage: it.only_subfaces()
1114
+ Traceback (most recent call last):
1115
+ ...
1116
+ ValueError: iterator not set to a face yet
1117
+
1118
+ Face iterator must not be in dual mode::
1119
+
1120
+ sage: it = C.face_generator(algorithm='dual')
1121
+ sage: _ = next(it)
1122
+ sage: it.only_subfaces()
1123
+ Traceback (most recent call last):
1124
+ ...
1125
+ ValueError: only possible when not in dual mode
1126
+
1127
+ Cannot run ``only_subfaces`` after ``ignore_subfaces``::
1128
+
1129
+ sage: it = C.face_generator()
1130
+ sage: _ = next(it)
1131
+ sage: it.ignore_subfaces()
1132
+ sage: it.only_subfaces()
1133
+ Traceback (most recent call last):
1134
+ ...
1135
+ ValueError: cannot only visit subsets after ignoring a face
1136
+ """
1137
+ if unlikely(self.dual):
1138
+ raise ValueError("only possible when not in dual mode")
1139
+ self.only_subsets()
1140
+
1141
+ def only_supfaces(self):
1142
+ r"""
1143
+ The iterator will visit all (remaining) faces
1144
+ containing the current face and then terminate.
1145
+
1146
+ EXAMPLES::
1147
+
1148
+ sage: P = polytopes.cross_polytope(3)
1149
+ sage: it = P.face_generator()
1150
+ sage: next(it).ambient_V_indices()
1151
+ (0, 1, 2, 3, 4, 5)
1152
+ sage: next(it).ambient_V_indices()
1153
+ ()
1154
+ sage: next(it).ambient_V_indices()
1155
+ (5,)
1156
+ sage: next(it).ambient_V_indices()
1157
+ (4,)
1158
+ sage: it.only_supfaces()
1159
+ sage: list(f.ambient_V_indices() for f in it)
1160
+ [(4, 5), (3, 4), (2, 4), (0, 4), (3, 4, 5), (2, 4, 5), (0, 3, 4), (0, 2, 4)]
1161
+
1162
+ ::
1163
+
1164
+ sage: P = polytopes.Birkhoff_polytope(4)
1165
+ sage: C = P.combinatorial_polyhedron()
1166
+ sage: it = C.face_generator(algorithm='dual')
1167
+ sage: next(it).ambient_V_indices()
1168
+ (23,)
1169
+ sage: next(it).ambient_V_indices()
1170
+ (22,)
1171
+ sage: it.only_supfaces()
1172
+ sage: all(22 in f.ambient_V_indices() for f in it)
1173
+ True
1174
+ """
1175
+ if unlikely(not self.dual):
1176
+ raise ValueError("only possible when in dual mode")
1177
+ self.only_subsets()
1178
+
1179
+ cdef int only_subsets(self) except -1:
1180
+ r"""
1181
+ Only visit sub-/supfaces of the current face and then
1182
+ terminate.
1183
+
1184
+ See :meth:`FaceIterator_base.only_subfaces` and
1185
+ :meth:`FaceIterator_base.only_supfaces`.
1186
+ """
1187
+ if unlikely(self.structure.face_status == FaceStatus.NOT_INITIALIZED):
1188
+ raise ValueError("iterator not set to a face yet")
1189
+ if unlikely(self.structure.face_status == FaceStatus.IGNORE_SUBSETS):
1190
+ raise ValueError("cannot only visit subsets after ignoring a face")
1191
+
1192
+ cdef face_list_t* faces = &self.structure.new_faces[self.structure.current_dimension]
1193
+ cdef size_t yet_to_visit = self.structure.yet_to_visit
1194
+
1195
+ if unlikely(yet_to_visit >= faces[0].n_faces
1196
+ or not faces_are_identical(faces[0].faces[yet_to_visit], self.structure.face)):
1197
+ raise ValueError("iterator is not set to the correct face")
1198
+
1199
+ swap_faces(faces[0].faces[yet_to_visit], faces[0].faces[faces[0].n_faces - 1])
1200
+
1201
+ self.structure.face_status = FaceStatus.ONLY_VISIT_SUBSETS
1202
+ self.structure.yet_to_visit = 0
1203
+ # This will work:
1204
+ # ``next_dimension`` will first call ``next_face_loop`` and then check
1205
+ # for the dimension. By this time the current dimension has changed.
1206
+ self.structure.highest_dimension = self.structure.current_dimension - 1
1207
+
1208
+ cdef inline CombinatorialFace next_face(self):
1209
+ r"""
1210
+ Set attribute ``face`` to the next face and return it as
1211
+ :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`.
1212
+ """
1213
+ self.next_dimension()
1214
+ if unlikely(self.structure.current_dimension > self.structure.highest_dimension):
1215
+ return None
1216
+ return CombinatorialFace(self)
1217
+
1218
+ cdef inline int next_dimension(self) except -1:
1219
+ r"""
1220
+ Set attribute ``face`` to the next face and return the dimension.
1221
+
1222
+ Will return the dimension of the polyhedron on failure.
1223
+
1224
+ The function calls :meth:`FaceIterator_base.next_face_loop` until a new
1225
+ face is set or until the iterator is consumed.
1226
+
1227
+ .. NOTE::
1228
+
1229
+ The face_iterator can be prevented from visiting any subfaces
1230
+ (or supfaces in dual mode) as in :meth:`FaceIterator_base.ignore_subfaces`
1231
+ and :meth`FaceIterator_base.ignore_supfaces`.
1232
+
1233
+ Those methods add the current face to ``visited_all`` before
1234
+ visiting sub-/supfaces instead of after. One cannot arbitrarily
1235
+ add faces to ``visited_all``, as visited_all has a maximal length.
1236
+ """
1237
+ return next_dimension(self.structure)
1238
+
1239
+ cdef inline int next_face_loop(self) except -1:
1240
+ r"""
1241
+ Set attribute ``face`` to the next face. On success return `1`.
1242
+ Otherwise `0`. Needs to be recalled then.
1243
+
1244
+ If ``self.current_dimension == self.dimension``, then the iterator is
1245
+ consumed.
1246
+ """
1247
+ return next_face_loop(self.structure)
1248
+
1249
+ cdef inline size_t n_atom_rep(self) except -1:
1250
+ r"""
1251
+ Compute the number of atoms in the current face by counting the
1252
+ number of set bits.
1253
+
1254
+ This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.n_atom_rep`
1255
+ """
1256
+ return n_atom_rep(self.structure)
1257
+
1258
+ cdef size_t set_coatom_rep(self) except -1:
1259
+ r"""
1260
+ Set ``coatom_rep`` to be the coatom-representation of the current face.
1261
+ Return its length.
1262
+
1263
+ This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_coatom_rep`
1264
+ """
1265
+ return bit_rep_to_coatom_rep(self.structure.face, self.coatoms.data, self.structure.coatom_rep)
1266
+
1267
+ cdef size_t set_atom_rep(self) except -1:
1268
+ r"""
1269
+ Set ``atom_rep`` to be the atom-representation of the current face.
1270
+ Return its length.
1271
+
1272
+ This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_atom_rep`
1273
+ """
1274
+ return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep)
1275
+
1276
+ cdef int find_face(self, face_t face) except -1:
1277
+ """
1278
+ Iterate until the current face is ``face``.
1279
+
1280
+ The value can then be obtained with :meth:`current`.
1281
+
1282
+ The iterator is assumed to be newly initialized or reset.
1283
+ See :meth:`FaceIterator_base._join_of_atoms` and
1284
+ :meth:`FaceIterator_base._meet_of_coatoms`.
1285
+ """
1286
+ cdef size_t n_atoms = face_len_atoms(face)
1287
+
1288
+ if n_atoms == self.coatoms.n_atoms():
1289
+ # The face is the universe.
1290
+ self.structure.face[0] = face[0]
1291
+ self.structure.face_status = FaceStatus.INITIALIZED
1292
+ self.structure.current_dimension = self.structure.dimension
1293
+ return 0
1294
+ elif n_atoms == 0:
1295
+ # The face is the empty face.
1296
+ self.structure.face[0] = face[0]
1297
+ self.structure.face_status = FaceStatus.INITIALIZED
1298
+ self.structure.current_dimension = -1
1299
+ return 0
1300
+
1301
+ self.next_dimension()
1302
+ while self.structure.current_dimension != self.structure.dimension:
1303
+ if face_issubset(face, self.structure.face):
1304
+ if face_issubset(self.structure.face, face):
1305
+ # Found our face.
1306
+ return 0
1307
+ else:
1308
+ # The face is not a subface/supface of the current face.
1309
+ self.ignore_subsets()
1310
+
1311
+ self.next_dimension()
1312
+
1313
+ raise ValueError("the face appears to be incorrect")
1314
+
1315
+
1316
+ cdef class FaceIterator(FaceIterator_base):
1317
+ r"""
1318
+ A class to iterate over all combinatorial faces of a polyhedron.
1319
+
1320
+ Construct all proper faces from the facets. In dual mode, construct all proper
1321
+ faces from the vertices. Dual will be faster for less vertices than facets.
1322
+
1323
+ INPUT:
1324
+
1325
+ - ``C`` -- a :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.base.CombinatorialPolyhedron`
1326
+ - ``dual`` -- if ``True``, then dual polyhedron is used for iteration
1327
+ (only possible for bounded Polyhedra)
1328
+ - ``output_dimension`` -- if not ``None``, then the face iterator will only yield
1329
+ faces of this dimension
1330
+
1331
+ .. SEEALSO::
1332
+
1333
+ :class:`FaceIterator`,
1334
+ :class:`FaceIterator_geom`,
1335
+ :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.base.CombinatorialPolyhedron`.
1336
+
1337
+ EXAMPLES:
1338
+
1339
+ Construct a face iterator::
1340
+
1341
+ sage: P = polytopes.cuboctahedron()
1342
+ sage: C = CombinatorialPolyhedron(P)
1343
+ sage: it = C.face_generator()
1344
+ sage: next(it)
1345
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron
1346
+
1347
+ Construct faces by the dual or not::
1348
+
1349
+ sage: it = C.face_generator(algorithm='primal')
1350
+ sage: next(it).dimension()
1351
+ 2
1352
+
1353
+ sage: it = C.face_generator(algorithm='dual')
1354
+ sage: next(it).dimension()
1355
+ 0
1356
+
1357
+ For unbounded polyhedra only non-dual iteration is possible::
1358
+
1359
+ sage: P = Polyhedron(rays=[[0,0,1], [0,1,0], [1,0,0]])
1360
+ sage: C = CombinatorialPolyhedron(P)
1361
+ sage: it = C.face_generator()
1362
+ sage: [face.ambient_Vrepresentation() for face in it]
1363
+ [(A vertex at (0, 0, 0),
1364
+ A ray in the direction (0, 1, 0),
1365
+ A ray in the direction (1, 0, 0)),
1366
+ (A vertex at (0, 0, 0),
1367
+ A ray in the direction (0, 0, 1),
1368
+ A ray in the direction (1, 0, 0)),
1369
+ (A vertex at (0, 0, 0),
1370
+ A ray in the direction (0, 0, 1),
1371
+ A ray in the direction (0, 1, 0)),
1372
+ (A vertex at (0, 0, 0), A ray in the direction (1, 0, 0)),
1373
+ (A vertex at (0, 0, 0), A ray in the direction (0, 1, 0)),
1374
+ (A vertex at (0, 0, 0),),
1375
+ (A vertex at (0, 0, 0), A ray in the direction (0, 0, 1))]
1376
+ sage: it = C.face_generator(algorithm='dual')
1377
+ Traceback (most recent call last):
1378
+ ...
1379
+ ValueError: dual algorithm only available for bounded polyhedra
1380
+
1381
+ Construct a face iterator only yielding dimension `2` faces::
1382
+
1383
+ sage: P = polytopes.permutahedron(5)
1384
+ sage: C = CombinatorialPolyhedron(P)
1385
+ sage: it = C.face_generator(dimension=2)
1386
+ sage: counter = 0
1387
+ sage: for _ in it: counter += 1
1388
+ sage: print ('permutahedron(5) has', counter,
1389
+ ....: 'faces of dimension 2')
1390
+ permutahedron(5) has 150 faces of dimension 2
1391
+ sage: C.f_vector()
1392
+ (1, 120, 240, 150, 30, 1)
1393
+
1394
+ In non-dual mode one can ignore all faces contained in the current face::
1395
+
1396
+ sage: P = polytopes.cube()
1397
+ sage: C = CombinatorialPolyhedron(P)
1398
+ sage: it = C.face_generator(algorithm='primal')
1399
+ sage: face = next(it)
1400
+ sage: face.ambient_H_indices()
1401
+ (5,)
1402
+ sage: it.ignore_subfaces()
1403
+ sage: [face.ambient_H_indices() for face in it]
1404
+ [(4,),
1405
+ (3,),
1406
+ (2,),
1407
+ (1,),
1408
+ (0,),
1409
+ (3, 4),
1410
+ (1, 4),
1411
+ (0, 4),
1412
+ (1, 3, 4),
1413
+ (0, 1, 4),
1414
+ (2, 3),
1415
+ (1, 3),
1416
+ (1, 2, 3),
1417
+ (1, 2),
1418
+ (0, 2),
1419
+ (0, 1, 2),
1420
+ (0, 1)]
1421
+
1422
+ sage: it = C.face_generator(algorithm='dual')
1423
+ sage: next(it)
1424
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron
1425
+ sage: it.ignore_subfaces()
1426
+ Traceback (most recent call last):
1427
+ ...
1428
+ ValueError: only possible when not in dual mode
1429
+
1430
+ In dual mode one can ignore all faces that contain the current face::
1431
+
1432
+ sage: it = C.face_generator(algorithm='dual')
1433
+ sage: next(it)
1434
+ A 0-dimensional face of a 3-dimensional combinatorial polyhedron
1435
+ sage: face = next(it)
1436
+ sage: face.ambient_V_indices()
1437
+ (6,)
1438
+ sage: [face.ambient_V_indices() for face in it]
1439
+ [(5,),
1440
+ (4,),
1441
+ (3,),
1442
+ (2,),
1443
+ (1,),
1444
+ (0,),
1445
+ (6, 7),
1446
+ (4, 7),
1447
+ (2, 7),
1448
+ (4, 5, 6, 7),
1449
+ (1, 2, 6, 7),
1450
+ (2, 3, 4, 7),
1451
+ (5, 6),
1452
+ (1, 6),
1453
+ (0, 1, 5, 6),
1454
+ (4, 5),
1455
+ (0, 5),
1456
+ (0, 3, 4, 5),
1457
+ (3, 4),
1458
+ (2, 3),
1459
+ (0, 3),
1460
+ (0, 1, 2, 3),
1461
+ (1, 2),
1462
+ (0, 1)]
1463
+
1464
+ sage: it = C.face_generator(algorithm='primal')
1465
+ sage: next(it)
1466
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron
1467
+ sage: it.ignore_supfaces()
1468
+ Traceback (most recent call last):
1469
+ ...
1470
+ ValueError: only possible when in dual mode
1471
+
1472
+ ALGORITHM:
1473
+
1474
+ The algorithm to visit all proper faces exactly once is roughly
1475
+ equivalent to the following. A (slightly generalized) description of the
1476
+ algorithm can be found in [KS2019]_.
1477
+
1478
+ Initialization::
1479
+
1480
+ faces = [set(facet) for facet in P.facets()]
1481
+ face_iterator(faces, [])
1482
+
1483
+ The function ``face_iterator`` is defined recursively. It visits all faces of
1484
+ the polyhedron `P`, except those contained in any of ``visited_all``.
1485
+ It assumes ``faces`` to be exactly those facets of `P`
1486
+ that are not contained in any of the ``visited_all``.
1487
+ It assumes ``visited_all`` to be some list of faces of
1488
+ a polyhedron `P_2`, which contains `P` as one of its faces::
1489
+
1490
+ def face_iterator(faces, visited_all):
1491
+ while facets:
1492
+ one_face = faces.pop()
1493
+ maybe_new_faces = [one_face.intersection(face) for face in faces]
1494
+ ...
1495
+
1496
+ At this point we claim that ``maybe_new_faces`` contains all facets of ``one_face``,
1497
+ which we have not visited before.
1498
+
1499
+ Proof: Let `F` be a facet of ``one_face``. We have a chain:
1500
+ `P \supset{}` ``one_face`` `{}\supset F`.
1501
+ By the diamond property, there exists a ``second_face`` with
1502
+ `P \supset{}` ``second_face`` `{}\supset F`.
1503
+
1504
+ Now either ``second_face`` is not an element of ``faces``:
1505
+ Hence ``second_face`` is contained in one of ``visited_all``.
1506
+ In particular, `F` is contained in ``visited_all``.
1507
+
1508
+ Or ``second_face`` is an element of ``faces``:
1509
+ Then, intersecting ``one_face`` with ``second_face`` gives ``F``.
1510
+
1511
+ This concludes the proof.
1512
+
1513
+ Moreover, if an element in ``maybe_new_faces`` is inclusion-maximal
1514
+ and not contained in any of the ``visited_all``, it is a facet of ``one_face``.
1515
+ Any facet in ``maybe_new_faces`` of ``one_face`` is inclusion-maximal.
1516
+
1517
+ Hence, in the following loop, an element ``face1`` in ``maybe_new_faces``
1518
+ is a facet of ``one_face`` if and only if it is not contained in another facet::
1519
+
1520
+ ...
1521
+ maybe_new_faces2 = []
1522
+ for i, face1 in enumerate(maybe_new_faces):
1523
+ if (all(not face1 < face2 for face2 in maybe_new_faces[:i])
1524
+ and all(not face1 <= face2 for face2 in maybe_new_faces[i+1:])):
1525
+ maybe_new_faces2.append(face1)
1526
+ ...
1527
+
1528
+ Now ``maybe_new_faces2`` contains only facets of ``one_face``
1529
+ and some faces contained in any of ``visited_all``.
1530
+ It also contains all the facets not contained in any of ``visited_all``.
1531
+
1532
+ We construct ``new_faces`` as the list of all facets of ``one_face``
1533
+ not contained in any of ``visited_all``::
1534
+
1535
+ ...
1536
+ new_faces = []
1537
+ for face1 in maybe_new_faces2:
1538
+ if all(not face1 < face2 for face2 in visited_all):
1539
+ new_faces.append(face1)
1540
+ ...
1541
+
1542
+ By induction we can apply the algorithm, to visit all
1543
+ faces of ``one_face`` not contained in ``visited_all``::
1544
+
1545
+ ...
1546
+ face_iterator(new_faces, visited_all)
1547
+ ...
1548
+
1549
+ Finally we visit ``one_face`` and add it to ``visited_all``::
1550
+
1551
+ ...
1552
+ visit(one_face)
1553
+ visited_all.append(one_face)
1554
+
1555
+ Note: At this point, we have visited exactly those faces,
1556
+ contained in any of the ``visited_all``. The function ends here.
1557
+
1558
+
1559
+ ALGORITHM for the special case that all intervals of the lattice not
1560
+ containing zero are boolean (e.g. when the polyhedron is simple):
1561
+
1562
+ We do not assume any other properties of our lattice in this case.
1563
+ Note that intervals of length 2 not containing zero, have exactly 2 elements now.
1564
+ But the atom-representation of faces might not be unique.
1565
+
1566
+ We do the following modifications:
1567
+
1568
+ - To check whether an intersection of faces is zero, we check whether the
1569
+ atom-representation is zero. Although not unique,
1570
+ it works to distinct from zero.
1571
+
1572
+ - The intersection of two (relative) facets has always codimension `1` unless empty.
1573
+
1574
+ - To intersect we now additionally unite the coatom representation.
1575
+ This gives the correct representation of the new face
1576
+ unless the intersection is zero.
1577
+
1578
+ - To mark a face as visited, we save its coatom representation.
1579
+
1580
+ - To check whether we have seen a face already, we check containment of the coatom representation.
1581
+ """
1582
+ def _repr_(self):
1583
+ r"""
1584
+ EXAMPLES::
1585
+
1586
+ sage: P = polytopes.associahedron(['A',3]) # needs sage.combinat
1587
+ sage: C = CombinatorialPolyhedron(P) # needs sage.combinat
1588
+ sage: C.face_generator() # needs sage.combinat
1589
+ Iterator over the proper faces of a 3-dimensional combinatorial polyhedron
1590
+
1591
+ sage: C.face_generator(1) # needs sage.combinat
1592
+ Iterator over the 1-faces of a 3-dimensional combinatorial polyhedron
1593
+ """
1594
+ if self.structure.output_dimension != -2:
1595
+ if self.dual:
1596
+ # output_dimension is stored with respect to the dual
1597
+ intended_dimension = self.structure.dimension - 1 - self.structure.output_dimension
1598
+ else:
1599
+ intended_dimension = self.structure.output_dimension
1600
+ output = "Iterator over the {}-faces".format(intended_dimension)
1601
+ else:
1602
+ output = "Iterator over the proper faces"
1603
+ return output + " of a {}-dimensional combinatorial polyhedron".format(self.structure.dimension)
1604
+
1605
+ def __next__(self):
1606
+ r"""
1607
+ Return the next face.
1608
+
1609
+ EXAMPLES::
1610
+
1611
+ sage: P = polytopes.cube()
1612
+ sage: C = CombinatorialPolyhedron(P)
1613
+ sage: it = C.face_generator()
1614
+ sage: [next(it) for _ in range(7)]
1615
+ [A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
1616
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
1617
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
1618
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
1619
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
1620
+ A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
1621
+ A 1-dimensional face of a 3-dimensional combinatorial polyhedron]
1622
+ """
1623
+ cdef CombinatorialFace face = self.next_face()
1624
+ if unlikely(self.structure.current_dimension > self.structure.highest_dimension):
1625
+ raise StopIteration
1626
+
1627
+ return face
1628
+
1629
+
1630
+ cdef class FaceIterator_geom(FaceIterator_base):
1631
+ r"""
1632
+ A class to iterate over all geometric faces of a polyhedron.
1633
+
1634
+ Construct all faces from the facets. In dual mode, construct all
1635
+ faces from the vertices. Dual will be faster for less vertices than facets.
1636
+
1637
+ INPUT:
1638
+
1639
+ - ``P`` -- an instance of :class:`~sage.geometry.polyhedron.base.Polyhedron_base`
1640
+ - ``dual`` -- if ``True``, then dual polyhedron is used for iteration
1641
+ (only possible for bounded Polyhedra)
1642
+ - ``output_dimension`` -- if not ``None``, then the FaceIterator will only yield
1643
+ faces of this dimension
1644
+
1645
+ EXAMPLES:
1646
+
1647
+ Construct a geometric face iterator::
1648
+
1649
+ sage: P = polytopes.cuboctahedron()
1650
+ sage: it = P.face_generator()
1651
+ sage: next(it)
1652
+ A 3-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 12 vertices
1653
+
1654
+ Construct faces by the dual or not::
1655
+
1656
+ sage: it = P.face_generator(algorithm='primal')
1657
+ sage: _ = next(it), next(it)
1658
+ sage: next(it).dim()
1659
+ 2
1660
+
1661
+ sage: it = P.face_generator(algorithm='dual')
1662
+ sage: _ = next(it), next(it)
1663
+ sage: next(it).dim()
1664
+ 0
1665
+
1666
+ For unbounded polyhedra only non-dual iteration is possible::
1667
+
1668
+ sage: P = Polyhedron(rays=[[0,0,1], [0,1,0], [1,0,0]])
1669
+ sage: it = P.face_generator()
1670
+ sage: [face.ambient_Vrepresentation() for face in it]
1671
+ [(A vertex at (0, 0, 0),
1672
+ A ray in the direction (0, 0, 1),
1673
+ A ray in the direction (0, 1, 0),
1674
+ A ray in the direction (1, 0, 0)),
1675
+ (),
1676
+ (A vertex at (0, 0, 0),
1677
+ A ray in the direction (0, 1, 0),
1678
+ A ray in the direction (1, 0, 0)),
1679
+ (A vertex at (0, 0, 0),
1680
+ A ray in the direction (0, 0, 1),
1681
+ A ray in the direction (1, 0, 0)),
1682
+ (A vertex at (0, 0, 0),
1683
+ A ray in the direction (0, 0, 1),
1684
+ A ray in the direction (0, 1, 0)),
1685
+ (A vertex at (0, 0, 0), A ray in the direction (1, 0, 0)),
1686
+ (A vertex at (0, 0, 0), A ray in the direction (0, 1, 0)),
1687
+ (A vertex at (0, 0, 0),),
1688
+ (A vertex at (0, 0, 0), A ray in the direction (0, 0, 1))]
1689
+ sage: it = P.face_generator(algorithm='dual')
1690
+ Traceback (most recent call last):
1691
+ ...
1692
+ ValueError: cannot iterate over dual of unbounded Polyedron
1693
+
1694
+ Construct a FaceIterator only yielding dimension `2` faces::
1695
+
1696
+ sage: P = polytopes.permutahedron(5)
1697
+ sage: it = P.face_generator(face_dimension=2)
1698
+ sage: counter = 0
1699
+ sage: for _ in it: counter += 1
1700
+ sage: print ('permutahedron(5) has', counter,
1701
+ ....: 'faces of dimension 2')
1702
+ permutahedron(5) has 150 faces of dimension 2
1703
+ sage: P.f_vector()
1704
+ (1, 120, 240, 150, 30, 1)
1705
+
1706
+ In non-dual mode one can ignore all faces contained in the current face::
1707
+
1708
+ sage: P = polytopes.cube()
1709
+ sage: it = P.face_generator(algorithm='primal')
1710
+ sage: _ = next(it), next(it)
1711
+ sage: face = next(it)
1712
+ sage: face.ambient_H_indices()
1713
+ (5,)
1714
+ sage: it.ignore_subfaces()
1715
+ sage: [face.ambient_H_indices() for face in it]
1716
+ [(4,),
1717
+ (3,),
1718
+ (2,),
1719
+ (1,),
1720
+ (0,),
1721
+ (3, 4),
1722
+ (1, 4),
1723
+ (0, 4),
1724
+ (1, 3, 4),
1725
+ (0, 1, 4),
1726
+ (2, 3),
1727
+ (1, 3),
1728
+ (1, 2, 3),
1729
+ (1, 2),
1730
+ (0, 2),
1731
+ (0, 1, 2),
1732
+ (0, 1)]
1733
+
1734
+ sage: it = P.face_generator(algorithm='dual')
1735
+ sage: _ = next(it), next(it)
1736
+ sage: next(it)
1737
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
1738
+ sage: it.ignore_subfaces()
1739
+ Traceback (most recent call last):
1740
+ ...
1741
+ ValueError: only possible when not in dual mode
1742
+
1743
+ In dual mode one can ignore all faces that contain the current face::
1744
+
1745
+ sage: P = polytopes.cube()
1746
+ sage: it = P.face_generator(algorithm='dual')
1747
+ sage: _ = next(it), next(it)
1748
+ sage: next(it)
1749
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
1750
+ sage: face = next(it)
1751
+ sage: face.ambient_V_indices()
1752
+ (6,)
1753
+ sage: [face.ambient_V_indices() for face in it]
1754
+ [(5,),
1755
+ (4,),
1756
+ (3,),
1757
+ (2,),
1758
+ (1,),
1759
+ (0,),
1760
+ (6, 7),
1761
+ (4, 7),
1762
+ (2, 7),
1763
+ (4, 5, 6, 7),
1764
+ (1, 2, 6, 7),
1765
+ (2, 3, 4, 7),
1766
+ (5, 6),
1767
+ (1, 6),
1768
+ (0, 1, 5, 6),
1769
+ (4, 5),
1770
+ (0, 5),
1771
+ (0, 3, 4, 5),
1772
+ (3, 4),
1773
+ (2, 3),
1774
+ (0, 3),
1775
+ (0, 1, 2, 3),
1776
+ (1, 2),
1777
+ (0, 1)]
1778
+
1779
+ sage: it = P.face_generator(algorithm='primal')
1780
+ sage: _ = next(it), next(it)
1781
+ sage: next(it)
1782
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices
1783
+ sage: it.ignore_supfaces()
1784
+ Traceback (most recent call last):
1785
+ ...
1786
+ ValueError: only possible when in dual mode
1787
+
1788
+ .. SEEALSO::
1789
+
1790
+ :class:`FaceIterator_base`.
1791
+ """
1792
+ def __init__(self, P, dual=None, output_dimension=None):
1793
+ r"""
1794
+ Initialize :class:`FaceIterator_base`.
1795
+
1796
+ See :class:`FaceIterator_base`.
1797
+
1798
+ EXAMPLES::
1799
+
1800
+ sage: P = polytopes.permutahedron(4)
1801
+ sage: it = P.face_generator() # indirect doctest
1802
+ sage: it
1803
+ Iterator over the faces of a 3-dimensional polyhedron in ZZ^4
1804
+ sage: TestSuite(sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator_geom).run()
1805
+ """
1806
+ self._requested_dim = output_dimension
1807
+ self.P = P
1808
+ # Base class only has __cinit__ and not __init__
1809
+ self.reset()
1810
+
1811
+ def reset(self):
1812
+ r"""
1813
+ Reset the iterator.
1814
+
1815
+ The iterator will start with the first face again.
1816
+
1817
+ EXAMPLES::
1818
+
1819
+ sage: P = polytopes.cube()
1820
+ sage: it = P.face_generator()
1821
+ sage: next(it).ambient_V_indices()
1822
+ (0, 1, 2, 3, 4, 5, 6, 7)
1823
+ sage: next(it).ambient_V_indices()
1824
+ ()
1825
+ sage: next(it).ambient_V_indices()
1826
+ (0, 3, 4, 5)
1827
+ sage: it.reset()
1828
+ sage: next(it).ambient_V_indices()
1829
+ (0, 1, 2, 3, 4, 5, 6, 7)
1830
+ sage: next(it).ambient_V_indices()
1831
+ ()
1832
+ sage: next(it).ambient_V_indices()
1833
+ (0, 3, 4, 5)
1834
+ """
1835
+ output_dimension = self._requested_dim
1836
+ P = self.P
1837
+
1838
+ if output_dimension is None:
1839
+ if P.dim() == -1:
1840
+ self._trivial_faces = 1 # the empty polyhedron, only yield the empty face
1841
+ else:
1842
+ self._trivial_faces = 2 # yield the universe, then the empty face, than all other faces
1843
+ elif output_dimension == P.dim():
1844
+ self._trivial_faces = 4 # only yield the full-dimensional face and no other faces
1845
+ elif output_dimension == -1:
1846
+ self._trivial_faces = 3 # only yield the empty face and no other faces
1847
+ elif output_dimension < -1 or output_dimension > P.dim():
1848
+ self._trivial_faces = -1 # don't yield any faces at all
1849
+ else:
1850
+ self._trivial_faces = 0 # yield the faces of the requested dimension
1851
+ FaceIterator_base.reset(self)
1852
+
1853
+ def _repr_(self):
1854
+ r"""
1855
+ EXAMPLES::
1856
+
1857
+ sage: P = polytopes.associahedron(['A',3]) # needs sage.combinat
1858
+ sage: P.face_generator() # needs sage.combinat
1859
+ Iterator over the faces of a 3-dimensional polyhedron in QQ^3
1860
+
1861
+ sage: P.face_generator(1) # needs sage.combinat
1862
+ Iterator over the 1-faces of a 3-dimensional polyhedron in QQ^3
1863
+ """
1864
+ if self._requested_dim is not None:
1865
+ output = "Iterator over the {}-faces".format(self._requested_dim)
1866
+ else:
1867
+ output = "Iterator over the faces"
1868
+ return output + " of a {}-dimensional polyhedron in {}".format(self.structure.dimension, self.P.parent()._repr_ambient_module())
1869
+
1870
+ def __next__(self):
1871
+ r"""
1872
+ Return the next face.
1873
+
1874
+ EXAMPLES::
1875
+
1876
+ sage: P = polytopes.cube()
1877
+ sage: it = P.face_generator()
1878
+ sage: [next(it) for _ in range(7)]
1879
+ [A 3-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 8 vertices,
1880
+ A -1-dimensional face of a Polyhedron in ZZ^3,
1881
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices,
1882
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices,
1883
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices,
1884
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices,
1885
+ A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices]
1886
+ """
1887
+ if unlikely(self._trivial_faces):
1888
+ if self._trivial_faces == -1:
1889
+ raise StopIteration
1890
+ if self._trivial_faces in (2, 4): # Return the polyhedron.
1891
+ if self._trivial_faces == 2:
1892
+ self._trivial_faces = 1 # Return the empty face next.
1893
+ else:
1894
+ self._trivial_faces = -1 # The iterator is exhausted.
1895
+ equations = [eq.index() for eq in self.P.equation_generator()]
1896
+ return PolyhedronFace(self.P, range(self.P.n_Vrepresentation()), equations)
1897
+ else: # Return the empty face.
1898
+ if self._trivial_faces == 1:
1899
+ self._trivial_faces = 0 # Return the proper faces next.
1900
+ else:
1901
+ self._trivial_faces = -1 # The iterator is exhausted.
1902
+ return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation()))
1903
+
1904
+ self.next_dimension()
1905
+ if unlikely(self.structure.current_dimension > self.structure.highest_dimension):
1906
+ raise StopIteration
1907
+ return self.current()
1908
+
1909
+ def current(self):
1910
+ r"""
1911
+ Retrieve the last value of :meth:`__next__`.
1912
+
1913
+ EXAMPLES::
1914
+
1915
+ sage: P = polytopes.octahedron()
1916
+ sage: it = P.face_generator()
1917
+ sage: _ = next(it), next(it)
1918
+ sage: next(it)
1919
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
1920
+ sage: it.current()
1921
+ A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex
1922
+ sage: next(it).ambient_V_indices() == it.current().ambient_V_indices()
1923
+ True
1924
+ """
1925
+ return combinatorial_face_to_polyhedral_face(self.P, FaceIterator_base.current(self))
1926
+
1927
+
1928
+ # Nogil definitions of crucial functions.
1929
+
1930
+ cdef inline int next_dimension(iter_t structure, size_t parallelization_depth=0) except -1 nogil:
1931
+ r"""
1932
+ See :meth:`FaceIterator.next_dimension`.
1933
+
1934
+ ``parallelization_depth`` determines when to stop,
1935
+ e.g. if it is ``1`` it will stop after having yield all faces of a facet
1936
+ """
1937
+ cdef int max_dim = structure.highest_dimension - parallelization_depth
1938
+ structure.face_status = FaceStatus.NOT_INITIALIZED
1939
+ while (not next_face_loop(structure)) and (structure.current_dimension <= max_dim):
1940
+ sig_check()
1941
+ structure._index += 1
1942
+ return structure.current_dimension
1943
+
1944
+ cdef inline int next_face_loop(iter_t structure) except -1 nogil:
1945
+ r"""
1946
+ See :meth:`FaceIterator.next_face_loop`.
1947
+ """
1948
+ if unlikely(structure.current_dimension == structure.dimension):
1949
+ # The function is not supposed to be called,
1950
+ # just prevent it from crashing.
1951
+ # Actually raising an error here results in a bad branch prediction.
1952
+ # But return -1 results in a crash with python 3.12
1953
+ raise StopIteration
1954
+
1955
+ # Getting ``[faces, n_faces, n_visited_all]`` according to dimension.
1956
+ cdef face_list_t* faces = &structure.new_faces[structure.current_dimension]
1957
+ cdef face_list_t* new_faces = &structure.new_faces[structure.current_dimension-1]
1958
+ cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension]
1959
+ cdef size_t n_faces = faces[0].n_faces
1960
+
1961
+ if (structure.output_dimension > -2) and (structure.output_dimension != structure.current_dimension):
1962
+ # If only a specific dimension was requested (i.e. ``output_dimension > -2``),
1963
+ # then we will not yield faces in other dimension.
1964
+ structure.yet_to_visit = 0
1965
+
1966
+ if structure.yet_to_visit:
1967
+ # Set ``face`` to the next face.
1968
+ structure.yet_to_visit -= 1
1969
+ structure.face[0] = faces[0].faces[structure.yet_to_visit][0]
1970
+ structure.face_status = FaceStatus.INITIALIZED
1971
+ return 1
1972
+
1973
+ if structure.current_dimension <= structure.lowest_dimension:
1974
+ # We will not yield the empty face.
1975
+ # We will not yield below requested dimension.
1976
+ structure.current_dimension += 1
1977
+ return 0
1978
+
1979
+ if n_faces <= 1:
1980
+ # There will be no more faces from intersections.
1981
+ structure.current_dimension += 1
1982
+ return 0
1983
+
1984
+ if not structure.first_time[structure.current_dimension]:
1985
+ # In this case there exists ``faces[0].faces[n_faces]``, of which we
1986
+ # have visited all faces, but which was not added to
1987
+ # ``visited_all`` yet.
1988
+
1989
+ if not faces[0].polyhedron_is_simple:
1990
+ # In case of a simple lattice, this step needs not to be applied:
1991
+ # Every element, except the lower bound, has a unique representation of coatoms in this case.
1992
+ # Hence, as the face is already removed from faces[0], any subfaces will not be visited.
1993
+ # (If we manually ignore subfaces, faces will still be added to visited_all).
1994
+ add_face_shallow(visited_all[0], faces[0].faces[n_faces])
1995
+ else:
1996
+ # Once we have visited all faces of ``faces[n_faces]``, we want
1997
+ # to add it to ``visited_all``.
1998
+ structure.first_time[structure.current_dimension] = False
1999
+
2000
+ # We will visit the last face now.
2001
+
2002
+ # Get the faces of codimension 1 contained in ``faces[n_faces-1]``,
2003
+ # which we have not yet visited.
2004
+ cdef size_t new_faces_counter
2005
+
2006
+ new_faces_counter = get_next_level(
2007
+ faces[0], new_faces[0], visited_all[0])
2008
+
2009
+ if new_faces_counter:
2010
+ # ``faces[n_faces]`` contains new faces.
2011
+ # We will visit them on next call, starting with codimension 1.
2012
+
2013
+ # Setting the variables correctly for next call of ``next_face_loop``.
2014
+ structure.current_dimension -= 1
2015
+ structure.first_time[structure.current_dimension] = True
2016
+ structure.visited_all[structure.current_dimension][0] = visited_all[0][0]
2017
+ structure.yet_to_visit = new_faces_counter
2018
+ return 0
2019
+ else:
2020
+ # ``faces.faces[n_faces-1]`` contains no new faces.
2021
+ # Hence there is no need to add it to ``visited_all``.
2022
+ # NOTE:
2023
+ # For the methods ``ignore_subfaces`` and ``ignore_supfaces``
2024
+ # this step needs to be done, as ``faces.faces[n_faces-1]`` might
2025
+ # have been added manually to ``visited_all``.
2026
+ # So this step is required to respect boundaries of ``visited_all``.
2027
+ structure.first_time[structure.current_dimension] = True
2028
+ return 0
2029
+
2030
+ cdef inline size_t n_atom_rep(iter_t structure) except -1 nogil:
2031
+ r"""
2032
+ See :meth:`FaceIterator.n_atom_rep`.
2033
+ """
2034
+ if structure.face_status:
2035
+ return face_len_atoms(structure.face)
2036
+
2037
+ # The face was not initialized properly.
2038
+ raise LookupError("``FaceIterator`` does not point to a face")
2039
+
2040
+ # Parallel iteration over the faces.
2041
+ # Currently only the f-vector is implemented, but slight
2042
+ # modifications would allow collecting other information as well.
2043
+
2044
+ cdef struct parallel_f_s:
2045
+ # A structure carrying things that each thread should have exclusive access to.
2046
+ size_t* f_vector
2047
+ size_t* current_job_id
2048
+
2049
+ # Keep track so that we can easily go from one job to the next.
2050
+ size_t* original_n_faces
2051
+ size_t* original_n_visited_all
2052
+
2053
+ ctypedef parallel_f_s parallel_f_t[1]
2054
+
2055
+ cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parallelization_depth, size_t *f_vector) except -1:
2056
+ """
2057
+ Compute the ``f_vector`` in parallel.
2058
+
2059
+ INPUT:
2060
+
2061
+ - ``structures`` -- one structure per thread
2062
+
2063
+ - ``num_threads`` -- the number of threads to use
2064
+
2065
+ - ``parallelization_depth`` -- the codimension at which the threads are released
2066
+
2067
+ - ``f_vector`` -- where the ``f_vector`` is output
2068
+ """
2069
+ # One job per face of codimension ``parallelization_depth``.
2070
+ cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth
2071
+ cdef size_t i
2072
+ cdef int j
2073
+ cdef int dim = structures[0].dimension
2074
+ f_vector[0] = 1 # Face iterator will only visit proper faces.
2075
+ f_vector[dim + 1] = 1 # Face iterator will only visit proper faces.
2076
+ if dim <= 0 or structures[0].n_coatoms == 0:
2077
+ # Iterator assumes at least one face and at least dimension 1.
2078
+ return 0
2079
+
2080
+ if num_threads == 0:
2081
+ num_threads = 1
2082
+
2083
+ cdef MemoryAllocator mem = MemoryAllocator()
2084
+
2085
+ # Setting up for each thread some storage space.
2086
+ cdef parallel_f_t* parallel_structs = \
2087
+ <parallel_f_t*> mem.allocarray(num_threads, sizeof(parallel_f_t))
2088
+
2089
+ for i in range(num_threads):
2090
+ # Partial f-vectors.
2091
+ parallel_structs[i].f_vector = \
2092
+ <size_t*> mem.calloc(dim+2, sizeof(size_t))
2093
+ parallel_structs[i].current_job_id = \
2094
+ <size_t*> mem.calloc(parallelization_depth+1, sizeof(size_t))
2095
+
2096
+ # Keeping back of the original number of faces allows faster starting the next job.
2097
+ parallel_structs[i].original_n_faces = \
2098
+ <size_t*> mem.calloc(parallelization_depth+1, sizeof(size_t))
2099
+ parallel_structs[i].original_n_faces[0] = \
2100
+ structures[0].new_faces[dim - 1].n_faces
2101
+
2102
+ parallel_structs[i].original_n_visited_all = \
2103
+ <size_t*> mem.calloc(parallelization_depth+1, sizeof(size_t))
2104
+ parallel_structs[i].original_n_visited_all[0] = \
2105
+ structures[0].visited_all[dim - 1].n_faces
2106
+
2107
+ for i in prange(n_jobs, schedule='dynamic', chunksize=1,
2108
+ num_threads=num_threads, nogil=True):
2109
+ _parallel_f_vector(structures[threadid()],
2110
+ parallelization_depth,
2111
+ parallel_structs[threadid()],
2112
+ i)
2113
+
2114
+ # Gather the results.
2115
+ for i in range(num_threads):
2116
+ for j in range(structures[0].dimension + 2):
2117
+ f_vector[j] += parallel_structs[i].f_vector[j]
2118
+
2119
+ cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth,
2120
+ parallel_f_t parallel_struct, size_t job_id) except -1 nogil:
2121
+ """
2122
+ Set up a job and then visit all faces.
2123
+ """
2124
+ cdef int max_dimension = structure.dimension - parallelization_depth
2125
+ cdef int d
2126
+ if prepare_face_iterator_for_partial_job(structure, parallelization_depth,
2127
+ parallel_struct, job_id):
2128
+ d = next_dimension(structure, parallelization_depth)
2129
+ while (d < max_dimension):
2130
+ parallel_struct.f_vector[d+1] += 1
2131
+ d = next_dimension(structure, parallelization_depth)
2132
+
2133
+ cdef inline int prepare_face_iterator_for_partial_job(
2134
+ iter_t structure, size_t parallelization_depth,
2135
+ parallel_f_t parallel_struct, size_t job_id) except -1 nogil:
2136
+ """
2137
+ Set ``structure`` according to ``job_id``.
2138
+
2139
+ ``job_id`` should be thought of as its digits with base ``structure.n_coatoms``
2140
+ padded to ``parallelization_depth``.
2141
+
2142
+ The first digit determines which facet to visit.
2143
+ The next digit determines which facet of the facet should be visited.
2144
+
2145
+ OUTPUT: ``1`` if the job exists and ``0`` otherwise
2146
+
2147
+ In addition, the first job treating a face will "visit" this face
2148
+ and increase the corresponding entry of the f-vector.
2149
+ """
2150
+ cdef int d
2151
+ cdef size_t current_depth
2152
+ if (not structure.first_time[structure.current_dimension]
2153
+ and structure.current_dimension == structure.dimension - parallelization_depth):
2154
+ # Act as if we had not visited faces in the last depth.
2155
+ # Set ``current_job_id[parallelization_depth - 1] = 0``.
2156
+ d = structure.dimension - parallelization_depth
2157
+ current_depth = parallelization_depth
2158
+
2159
+ # Recover all faces.
2160
+ structure.new_faces[d].n_faces = \
2161
+ parallel_struct.original_n_faces[current_depth -1]
2162
+ structure.visited_all[d].n_faces = \
2163
+ parallel_struct.original_n_visited_all[current_depth -1]
2164
+ structure.first_time[d] = True
2165
+ structure.yet_to_visit = 0 # just to be on the safe side
2166
+
2167
+ parallel_struct.current_job_id[current_depth -1] = 0
2168
+
2169
+ # If the job does not exist, we will set the next value to ``-1``.
2170
+ if parallel_struct.original_n_faces[current_depth -1] == 0:
2171
+ parallel_struct.current_job_id[current_depth] = -1
2172
+ else:
2173
+ parallel_struct.current_job_id[current_depth] = 0
2174
+
2175
+ cdef size_t n_coatoms = structure.n_coatoms
2176
+ cdef size_t job_id_c
2177
+ cdef size_t i
2178
+ cdef size_t new_faces_counter
2179
+
2180
+ for current_depth in range(1, parallelization_depth + 1):
2181
+ d = structure.dimension - current_depth
2182
+
2183
+ # Get the corresponding digit of ``job_id``.
2184
+ job_id_c = get_digit(job_id, current_depth - 1, parallelization_depth, n_coatoms)
2185
+
2186
+ # Set ``current_job_id[current_depth - 1]`` to ``job_id_c`` if possible.
2187
+
2188
+ if job_id_c != parallel_struct.current_job_id[current_depth - 1]:
2189
+ # Set ``current_job_id[current_depth -1] = 0``.
2190
+ structure.current_dimension = d
2191
+ structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1]
2192
+ structure.visited_all[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1]
2193
+ parallel_struct.current_job_id[current_depth -1] = 0
2194
+
2195
+ # If the job does not exist, we will set the next value to ``-1``.
2196
+ if parallel_struct.original_n_faces[current_depth -1] == 0:
2197
+ parallel_struct.current_job_id[current_depth] = -1
2198
+ else:
2199
+ parallel_struct.current_job_id[current_depth] = 0
2200
+
2201
+ structure.first_time[d] = True
2202
+ structure.yet_to_visit = 0 # just to be on the safe side
2203
+
2204
+ if parallel_struct.current_job_id[current_depth] == -1:
2205
+ # The job does not exist.
2206
+ return 0
2207
+
2208
+ if job_id_c > parallel_struct.current_job_id[current_depth -1]:
2209
+ if job_id_c >= structure.new_faces[d].n_faces:
2210
+ # The job does not exist.
2211
+ return 0
2212
+
2213
+ for i in range(job_id_c):
2214
+ # Fast forwarding the jobs.
2215
+
2216
+ if not structure.new_faces[d].polyhedron_is_simple:
2217
+ # In case of a simple lattice, this step needs not to be applied:
2218
+ # Every element, except the lower bound, has a unique representation of coatoms in this case.
2219
+ # Hence, as the face is already removed from faces[0], any subfaces will not be visited.
2220
+ # (If we manually ignore subfaces, faces will still be added to visited_all).
2221
+ add_face_shallow(structure.visited_all[d], structure.new_faces[d].faces[structure.new_faces[d].n_faces -1])
2222
+
2223
+ structure.new_faces[d].n_faces -= 1
2224
+
2225
+ parallel_struct.current_job_id[current_depth -1] = job_id_c
2226
+
2227
+ # Apparently the face exists. We add it to the f-vector, if it is the very first job for the face.
2228
+ if job_id == 0 or get_digit(job_id -1, current_depth -1, parallelization_depth, n_coatoms) != job_id_c:
2229
+ # Visit ``structure.new_faces[d].faces[structure.new_faces[d].n_faces - 1]
2230
+ parallel_struct.f_vector[d + 1] += 1
2231
+
2232
+ if structure.current_dimension == d:
2233
+ structure.yet_to_visit = 0
2234
+
2235
+ if structure.new_faces[d].n_faces == 0:
2236
+ # The job will not exist.
2237
+ parallel_struct.current_job_id[current_depth] = -1
2238
+ return 0
2239
+
2240
+ new_faces_counter = get_next_level(
2241
+ structure.new_faces[d], structure.new_faces[d-1], structure.visited_all[d])
2242
+
2243
+ if new_faces_counter:
2244
+ # Setting the variables correctly, for the next call.
2245
+ structure.current_dimension -= 1
2246
+ structure.first_time[d-1] = True
2247
+ structure.visited_all[d-1][0] = structure.visited_all[d][0]
2248
+ structure.yet_to_visit = new_faces_counter
2249
+ for i in range(current_depth, parallelization_depth + 1):
2250
+ parallel_struct.current_job_id[i] = 0
2251
+ parallel_struct.original_n_faces[current_depth] = new_faces_counter
2252
+ parallel_struct.original_n_visited_all[current_depth] = structure.visited_all[d-1].n_faces
2253
+ else:
2254
+ # The job does not exist.
2255
+ parallel_struct.current_job_id[current_depth] = -1
2256
+ return 0
2257
+
2258
+ if structure.current_dimension != structure.dimension - parallelization_depth - 1:
2259
+ return 0
2260
+
2261
+ return 1
2262
+
2263
+ cdef inline size_t get_digit(size_t job_id, size_t pos, size_t padto, size_t base) noexcept nogil:
2264
+ """
2265
+ Get the digit ``pos`` of ``job_id`` with base ``base``
2266
+ padding the number of digits to ``pad_to``.
2267
+
2268
+ Digits are enumerated started with ``0``.
2269
+ """
2270
+ # Remove the last ``parallelization_depth - pos - 1`` digits.
2271
+ cdef size_t current_output = job_id / base ** (padto - pos - 1)
2272
+
2273
+ # Remove all digits before our current digit, which is digit ``pos``.
2274
+ return current_output % base