passagemath-polyhedra 10.6.31rc3__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_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.
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +206 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_polyhedra.libs/libgmp-6e109695.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- 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
|