diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index 2c4fe2b674e..0a2245e7688 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -37,8 +37,8 @@ cdef class CombinatorialPolyhedron(SageObject): cdef ListOfAllFaces _all_faces # class to generate Hasse diagram incidences cdef tuple _mem_tuple # stores MemoryAllocators for edges, ridges etc. - cdef FaceIterator _face_iter(self, bint dual) - cdef int _calculate_f_vector(self) except -1 - cdef int _calculate_edges(self, dual) except -1 - cdef int _calculate_ridges(self, dual) except -1 - cdef int _calculate_face_lattice_incidences(self) except -1 + cdef FaceIterator _face_iter(self, bint dual, int dimension) + cdef int _compute_f_vector(self) except -1 + cdef int _compute_edges(self, dual) except -1 + cdef int _compute_ridges(self, dual) except -1 + cdef int _compute_face_lattice_incidences(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index feb10538964..c61a4980739 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1,32 +1,20 @@ r""" -CombinatorialPolyhedron gathers several algorithms of Polyhedra depending only -on the vertex-facet incidences. +CombinatorialPolyhedron -Most importanly, this module offers a quick face iterator, which can be used -to calculate f-vector, edges, ridges and even the face lattice. +This module gathers algorithms for polyhedra that only depend on the +vertex-facet incidences. -The FaceIterator would work on every atomic and coatomic lattice, where every -interval of length two has exactly 4 elements (known as the diamond property). +Most importantly, one can construct a fast :class:`.face_iterator.FaceIterator`. +This module uses this construction to quickly generate +- the f-vector, +- the edges, +- the ridges, +- and the face lattice. -It also works on every lattice that is obtained by from such a lattice by -deleting all elements but the empty face contained in some of the coatoms. -Important examples are face lattices of unbounded polyhedra. - -Representations in this module: +Terminology in this module: - Vertices -- ``[vertices, rays, lines]`` of the polyhedron. - Facets -- facets of the polyhedron. -- Coatoms -- the faces from which all others are constructed in - the algorithm. This will be facets or vertices. - In non-dual mode, faces are constructed as - intersections of the facets. In dual mode, the are - constructed theoretically as joins of vertices. - The coatoms are reprsented as incidences with the - atoms they contain. -- Atoms -- facets or vertices depending on application of algorithm. - Atoms are reprsented as incidences of coatoms they - are contained in. - - Vertex-Representation -- represents a face by a list of vertices it contains. - Facet-Representation -- represents a face by a list of facets it is contained in. - Bit-Representation -- represents incidences as ``uint64_t``-array, where @@ -34,11 +22,57 @@ Representations in this module: be trailing zeros, to fit alignment-requirements. In most instances, faces are represented by the Bit-representation, where each bit corresponds to - an atom. + a vertex or facet. Thus Bit-representation can be + vertex-representation or facet-representation depending on + context. + +EXAMPLES: + +Construction:: + + sage: P = polytopes.hypercube(4) + sage: C = CombinatorialPolyhedron(P); C + Combinatorial type of a polyhedron of dimension 4 with 16 vertices + +Obtaining edges and ridges:: + + sage: C.edges()[:2] + ((A vertex at (1, 1, 1, -1), A vertex at (1, 1, 1, 1)), + (A vertex at (1, 1, -1, 1), A vertex at (1, 1, 1, 1))) + sage: C.edges(names=False)[:2] + ((14, 15), (13, 15)) + + sage: C.ridges()[:2] + ((An inequality (0, 0, 1, 0) x + 1 >= 0, + An inequality (0, 0, 0, 1) x + 1 >= 0), + (An inequality (0, 1, 0, 0) x + 1 >= 0, + An inequality (0, 0, 0, 1) x + 1 >= 0)) + sage: C.ridges(names=False)[:2] + ((6, 7), (5, 7)) + +Edge-graph and ridge-graph:: + + sage: C.edge_graph() + Graph on 16 vertices + sage: C.ridge_graph() + Graph on 8 vertices + +Face lattice:: + + sage: C.face_lattice() + Finite lattice containing 82 elements + +:class:`.face_iterator.FaceIterator`:: + + sage: C.face_iter() + Iterator over the proper faces of a polyhedron of dimension 4 + + sage: C.face_iter(2) + Iterator over the 2-faces of a polyhedron of dimension 4 AUTHOR: -- Jonathan Kliem (2019-03) +- Jonathan Kliem (2019-04) """ #***************************************************************************** @@ -107,9 +141,9 @@ cdef class CombinatorialPolyhedron(SageObject): - ``data`` -- a ``[list, tuple, iterator]`` of facets, each facet given as a list of ``[vertices, rays, lines]`` - if the Polyhedron is unbounded, then rays and lines are required + if the polyhedron is unbounded, then rays and lines are required if the Polyehdron contains no lines, the rays can be thought of as - the vertices of the facets deleted from a bounded Polyhedron + the vertices of the facets deleted from a bounded polyhedron see :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` on how to use rays and lines @@ -124,16 +158,16 @@ cdef class CombinatorialPolyhedron(SageObject): or - - ``data`` -- an Integer, representing the dimension of a Polyhedron equal + - ``data`` -- an Integer, representing the dimension of a polyhedron equal to its affine hull EXAMPLES: - Input is Polyhedron:: + Input is :meth:`Polyhedron`:: sage: P = polytopes.cube() sage: CombinatorialPolyhedron(P) - Combinatorial Type of a Polyhedron of dimension 3 with 8 vertices + Combinatorial type of a polyhedron of dimension 3 with 8 vertices Input is a LatticePolytope:: @@ -141,13 +175,13 @@ cdef class CombinatorialPolyhedron(SageObject): ....: (-1,0,0), (0,-1,0), (0,0,-1)] sage: L = LatticePolytope(points) sage: CombinatorialPolyhedron(L) - Combinatorial Type of a Polyhedron of dimension 3 with 6 vertices + Combinatorial type of a polyhedron of dimension 3 with 6 vertices Input is an incidence matrix:: sage: data = Polyhedron(rays=[[0,1]]).incidence_matrix() sage: CombinatorialPolyhedron(data, nr_lines=0) - Combinatorial Type of a Polyhedron of dimension 1 with 1 vertices + Combinatorial type of a polyhedron of dimension 1 with 1 vertices sage: C = CombinatorialPolyhedron(data, vertices=['myvertex'], ....: facets=['myfacet'], nr_lines=0) sage: C.Vrepresentation() @@ -158,7 +192,7 @@ cdef class CombinatorialPolyhedron(SageObject): You can also give the facets explicitely:: sage: CombinatorialPolyhedron(((1,2,3),(1,2,4),(1,3,4),(2,3,4))) - Combinatorial Type of a Polyhedron of dimension 3 with 4 vertices + Combinatorial type of a polyhedron of dimension 3 with 4 vertices sage: facetnames = ['facet0', 'facet1', 'facet2', 'myfacet3'] sage: facetinc = ((1,2,3),(1,2,4),(1,3,4),(2,3,4)) sage: C = CombinatorialPolyhedron(facetinc, facets=facetnames) @@ -179,9 +213,9 @@ cdef class CombinatorialPolyhedron(SageObject): Specifying the number of lines is important:: sage: P = Polyhedron(ieqs=[[1,-1,0],[1,1,0]]) - sage: C = CombinatorialPolyhedron(P) #this works fine + sage: C = CombinatorialPolyhedron(P) # this works fine sage: C - Combinatorial Type of a Polyhedron of dimension 2 with 0 vertices + Combinatorial type of a polyhedron of dimension 2 with 0 vertices sage: data = P.incidence_matrix() sage: vert = P.Vrepresentation() @@ -189,7 +223,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(data, vertices=vert) sage: C - Combinatorial Type of a Polyhedron of dimension 2 with 3 vertices + Combinatorial type of a polyhedron of dimension 2 with 3 vertices sage: C.f_vector() (1, 1, 2, 1) sage: C.vertices() @@ -201,18 +235,18 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(data, vertices=vert, nr_lines=1) sage: C - Combinatorial Type of a Polyhedron of dimension 2 with 0 vertices + Combinatorial type of a polyhedron of dimension 2 with 0 vertices sage: C.f_vector() (1, 0, 2, 1) sage: C.vertices() () - Initialization from Polyhedron will automatically specify number of lines:: + Initialization from polyhedron will automatically specify number of lines:: sage: P = Polyhedron(rays=[[1,0],[0,1]]) sage: C = CombinatorialPolyhedron(P) # this works fine sage: C - Combinatorial Type of a Polyhedron of dimension 2 with 1 vertices + Combinatorial type of a polyhedron of dimension 2 with 1 vertices sage: data = P.incidence_matrix() sage: vert = P.Vrepresentation() @@ -220,7 +254,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(data, vertices=vert) sage: C - Combinatorial Type of a Polyhedron of dimension 2 with 3 vertices + Combinatorial type of a polyhedron of dimension 2 with 3 vertices sage: C.f_vector() (1, 1, 2, 1) sage: C.vertices() @@ -230,7 +264,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(data, vertices=vert, nr_lines=0) sage: C - Combinatorial Type of a Polyhedron of dimension 2 with 1 vertices + Combinatorial type of a polyhedron of dimension 2 with 1 vertices sage: C.f_vector() (1, 1, 2, 1) sage: C.vertices() @@ -292,11 +326,11 @@ cdef class CombinatorialPolyhedron(SageObject): else: # Input is different from ``Polyhedron`` and ``LatticePolytope``. if nr_lines is None: - # According to input, the Polyhedron is bounded. + # According to input, the polyhedron is bounded. self._unbounded = False self._nr_lines = 0 else: - # According to input, the Polyhedron is unbounded. + # According to input, the polyhedron is unbounded. self._unbounded = True self._nr_lines = int(nr_lines) @@ -340,10 +374,10 @@ cdef class CombinatorialPolyhedron(SageObject): self._nr_facets = self.bitrep_facets.nr_faces elif isinstance(data, Integer): - # To construct a trivial Polyhedron, equal to its affine hull, + # To construct a trivial polyhedron, equal to its affine hull, # one can give an Integer as Input. if data < -1: - ValueError("any Polyhedron must have dimension at least -1") + ValueError("any polyhedron must have dimension at least -1") self._nr_facets = 0 self._dimension = data @@ -392,36 +426,36 @@ cdef class CombinatorialPolyhedron(SageObject): def _repr_(self): r""" - Return a description of the Combinatorial Polyhedron. + Return a description of the combinatorial polyhedron. EXAMPLES:: sage: P = polytopes.simplex() sage: C = CombinatorialPolyhedron(P) sage: C._repr_() - 'Combinatorial Type of a Polyhedron of dimension 3 with 4 vertices' + 'Combinatorial type of a polyhedron of dimension 3 with 4 vertices' sage: P = Polyhedron(vertices=[]) sage: C = CombinatorialPolyhedron(P) sage: C._repr_() - 'Combinatorial Type of a Polyhedron of dimension -1 with 0 vertices' + 'Combinatorial type of a polyhedron of dimension -1 with 0 vertices' sage: P = Polyhedron(vertices=[[0,0]]) sage: C = CombinatorialPolyhedron(P) sage: C._repr_() - 'Combinatorial Type of a Polyhedron of dimension 0 with 1 vertices' + 'Combinatorial type of a polyhedron of dimension 0 with 1 vertices' sage: P = Polyhedron(lines=[[0,0,1],[0,1,0]]) sage: C = CombinatorialPolyhedron(P) sage: C._repr_() - 'Combinatorial Type of a Polyhedron of dimension 2 with 0 vertices' + 'Combinatorial type of a polyhedron of dimension 2 with 0 vertices' sage: P = Polyhedron(rays=[[1,0,0],[0,1,0],[-1,0,0]]) sage: C = CombinatorialPolyhedron(P) sage: C._repr_() - 'Combinatorial Type of a Polyhedron of dimension 2 with 0 vertices' + 'Combinatorial type of a polyhedron of dimension 2 with 0 vertices' """ - return "Combinatorial Type of a Polyhedron of "\ + return "Combinatorial type of a polyhedron of "\ "dimension %s with %s vertices" \ % (self.dimension(), self.nr_vertices()) @@ -523,7 +557,7 @@ cdef class CombinatorialPolyhedron(SageObject): def dimension(self): r""" - Return the dimension of the Polyhedron. + Return the dimension of the polyhedron. EXAMPLES:: @@ -557,19 +591,19 @@ cdef class CombinatorialPolyhedron(SageObject): alarm! """ if self._dimension == -2: - # Dimension not calculated yet. + # Dimension not computed yet. if self._nr_facets == 0: - # The dimension of a trivial Polyhedron is assumed to contain + # The dimension of a trivial polyhedron is assumed to contain # exactly one "vertex" and for each dimension one "line" as in # :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` self._dimension = self._length_Vrep - 1 elif self._unbounded or self._nr_facets <= self._length_Vrep: - self._dimension = self.bitrep_facets.calculate_dimension() + self._dimension = self.bitrep_facets.compute_dimension() else: - # If the Polyhedron has many facets, + # If the polyhedron has many facets, # calculating the dimenion of the dual will be faster. - # The dual exists, if the Polyhedron is bounded. - self._dimension = self.bitrep_facets.calculate_dimension() + # The dual exists, if the polyhedron is bounded. + self._dimension = self.bitrep_facets.compute_dimension() return smallInteger(self._dimension) def nr_vertices(self): @@ -613,10 +647,10 @@ cdef class CombinatorialPolyhedron(SageObject): 1 """ if self.dimension() == 0: - # This specific trivial Polyhedron needs special attention. + # This specific trivial polyhedron needs special attention. return Integer(1) elif not self._unbounded: - # In the unbounded case, we need to actually calculated the vertices, + # In the unbounded case, we need to actually computed the vertices, # the the V-representation contains also ``lines`` and ``rays``. return Integer(self._length_Vrep) else: @@ -626,7 +660,7 @@ cdef class CombinatorialPolyhedron(SageObject): r""" Return the elements in the ``Vrepresentation`` that are vertices. - In case of an unbounded Polyhedron, there might be lines and + In case of an unbounded polyhedron, there might be lines and rays in the Vrepresentation. If ``names`` is set to ``False``, then the vertices are given by @@ -671,7 +705,7 @@ cdef class CombinatorialPolyhedron(SageObject): (A vertex at (0, 0),) """ if unlikely(self.dimension() == 0): - # Handling the case of a trivial Polyhedron of dimension `0`. + # Handling the case of a trivial polyhedron of dimension `0`. if names and self._V: return (self._V[0],) else: @@ -728,7 +762,7 @@ cdef class CombinatorialPolyhedron(SageObject): 1 """ if unlikely(self.dimension() == 0): - # This trivial Polyhedron needs special attention. + # This trivial polyhedron needs special attention. return Integer(1) return Integer(self._nr_facets) @@ -797,7 +831,7 @@ cdef class CombinatorialPolyhedron(SageObject): def edges(self, names=True): r""" - Return the edges of the Polyhedron, i.e. the rank 1 faces. + Return the edges of the polyhedron, i.e. the rank 1 faces. If ``names`` is set to ``False``, then the vertices in the edges are given by their indices in the Vrepresentation. @@ -867,16 +901,16 @@ cdef class CombinatorialPolyhedron(SageObject): """ cdef size_t len_edge_list = self._length_edges_list if self._edges is NULL: - # Calculate the edges. + # compute the edges. if self._unbounded: - self._calculate_edges(dual=False) + self._compute_edges(dual=False) elif self._length_Vrep > self._nr_facets*self._nr_facets: # This is a wild estimate # that in this case it is better not to use the dual. - self._calculate_edges(dual=False) + self._compute_edges(dual=False) else: # In most bounded cases, one should use the dual. - self._calculate_ridges(dual=True) + self._compute_ridges(dual=True) if self._edges is NULL: raise ValueError('could not determine edges') @@ -927,7 +961,7 @@ cdef class CombinatorialPolyhedron(SageObject): r""" Return the ridges. - The ridges of a Polyhedron are the faces + The ridges of a polyhedron are the faces contained in exactly two facets. To obtain all faces of codimnesion 1 use @@ -937,7 +971,7 @@ cdef class CombinatorialPolyhedron(SageObject): INPUT: - - ``add_equalities`` -- if ``True``, then equalities of the Polyhedron + - ``add_equalities`` -- if ``True``, then equalities of the polyhedron will be added - ``names`` -- if ``False``, then the facets are given by their indices @@ -995,7 +1029,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: P = Polyhedron(rays=[[1,0]]) sage: C = CombinatorialPolyhedron(P) sage: C - Combinatorial Type of a Polyhedron of dimension 1 with 1 vertices + Combinatorial type of a polyhedron of dimension 1 with 1 vertices sage: C.ridges() () sage: it = C.face_iter(0) @@ -1019,16 +1053,16 @@ cdef class CombinatorialPolyhedron(SageObject): """ cdef size_t len_ridge_list = self._length_edges_list if self._ridges is NULL: - # Calculate the ridges. + # compute the ridges. if self._unbounded: - self._calculate_ridges(dual=False) + self._compute_ridges(dual=False) elif self._length_Vrep*self._length_Vrep < self._nr_facets: # This is a wild estimate # that in this case it is better to use the dual. - self._calculate_edges(dual=True) + self._compute_edges(dual=True) else: # In most bounded cases, one should not use the dual. - self._calculate_ridges(dual=False) + self._compute_ridges(dual=False) if self._ridges is NULL: raise ValueError('could not determine ridges') nr_ridges = self._nr_ridges @@ -1069,7 +1103,7 @@ cdef class CombinatorialPolyhedron(SageObject): r""" Return the ridge graph. - The ridge graph of a Polyhedron consists of + The ridge graph of a polyhedron consists of ridges as edges and facets as vertices. If ``names`` is ``False``, the ``vertices`` of the graph will @@ -1086,7 +1120,7 @@ cdef class CombinatorialPolyhedron(SageObject): def f_vector(self): r""" - Calculate the ``f_vector`` of the Polyhedron. + Compute the ``f_vector`` of the polyhedron. The ``f_vector`` contains the number of faces of dimension `k` for each `k` in ``range(-1, self.dimension() + 1)``. @@ -1094,7 +1128,7 @@ cdef class CombinatorialPolyhedron(SageObject): .. NOTE:: To obtain edges and/or ridges as well, first do so. This might - already calculate the ``f_vector``. + already compute the ``f_vector``. EXAMPLES:: @@ -1149,7 +1183,7 @@ cdef class CombinatorialPolyhedron(SageObject): 1) """ if not self._f_vector: - self._calculate_f_vector() + self._compute_f_vector() if not self._f_vector: raise ValueError("could not determine f_vector") return self._f_vector @@ -1245,21 +1279,23 @@ cdef class CombinatorialPolyhedron(SageObject): else: dual = True - face_iter = self._face_iter(int(dual)) - if dimension is not None: - # Setting the face iterator to return only requested dimension. - face_iter.set_request_dimension(dimension) - return face_iter + return FaceIterator(self, dual, output_dimension=dimension) - cdef FaceIterator _face_iter(self, bint dual): + cdef FaceIterator _face_iter(self, bint dual, int dimension): r""" A method to obtain the FaceIterator as Cython object. + ``dimension`` is the ``output_dimension`` of :class:`FaceIterator`. + If ``dimension == -2`` this will indicate no ``output_dimension``. + See :meth:`CombinatorialPolyhedron.face_iter` """ if dual and self._unbounded: - raise ValueError("cannot iterate over dual of unbounded Polyhedron") - return FaceIterator(self, dual) + raise ValueError("cannot iterate over dual of unbounded polyhedron") + if dimension == -2: + return FaceIterator(self, dual) + else: + return FaceIterator(self, dual, output_dimension=dimension) def face_lattice(self): r""" @@ -1319,8 +1355,8 @@ cdef class CombinatorialPolyhedron(SageObject): True """ if not self._face_lattice_incidences: - # Calculate all incidences. - self._calculate_face_lattice_incidences() + # compute all incidences. + self._compute_face_lattice_incidences() if self._face_lattice_incidences is NULL: raise TypeError("could not determine face lattice") @@ -1598,14 +1634,14 @@ cdef class CombinatorialPolyhedron(SageObject): # Let ``_all_faces`` determine facet-representation. return self._all_faces.facet_repr(dim, newindex, names=names) - cdef int _calculate_f_vector(self) except -1: + cdef int _compute_f_vector(self) except -1: r""" - Calculate the ``f_vector`` of the Polyhedron. + Compute the ``f_vector`` of the polyhedron. See :meth:`f_vector`. """ if self._f_vector: - return 0 # There is no need to recalculate the f_vector. + return 0 # There is no need to recompute the f_vector. cdef bint dual if self._unbounded or self._nr_facets <= self._length_Vrep: @@ -1614,7 +1650,7 @@ cdef class CombinatorialPolyhedron(SageObject): else: # In this case the dual approach is faster. dual = True - cdef FaceIterator face_iter = self._face_iter(dual) + cdef FaceIterator face_iter = self._face_iter(dual, -2) cdef int dim = self.dimension() cdef int d # dimension of the current face of the iterator @@ -1636,32 +1672,32 @@ cdef class CombinatorialPolyhedron(SageObject): # Copy ``f_vector``. if dual: - # We have calculated the ``f_vector`` of the dual. + # We have computed the ``f_vector`` of the dual. # Reverse it: self._f_vector = \ tuple(smallInteger(f_vector[dim+1-i]) for i in range(dim+2)) else: self._f_vector = tuple(smallInteger(f_vector[i]) for i in range(dim+2)) - cdef int _calculate_edges(self, dual) except -1: + cdef int _compute_edges(self, dual) except -1: r""" - Calculate the edges of the Polyhedron. + Compute the edges of the polyhedron. - If ``dual`` is ``True``, calculate the edges of the dual. In this case, - this will also calculate the ``f_vector``, if unknown. + If ``dual`` is ``True``, compute the edges of the dual. In this case, + this will also compute the ``f_vector``, if unknown. See :meth:`CombinatorialPolyhedron.edges` and :meth:`CombinatorialPolyhedron.ridges`. """ if (self._edges is not NULL and not dual) or (self._ridges is not NULL and dual): - return 0 # There is no need to recalculate. + return 0 # There is no need to recompute. cdef MemoryAllocator mem = MemoryAllocator() - cdef FaceIterator face_iter = self._face_iter(dual) + cdef FaceIterator face_iter cdef size_t len_edge_list = self._length_edges_list cdef int dim = self.dimension() cdef int d # dimension of the current face of ``FaceIterator`` - cdef size_t *f_vector # calculate f_vector, if not done already - cdef bint is_f_vector # True if f_vector was calculated previously + cdef size_t *f_vector # compute f_vector, if not done already + cdef bint is_f_vector # True if f_vector was computed previously # For each edge we determine its location in ``edges`` # by ``edges[one][two]`` @@ -1674,7 +1710,7 @@ cdef class CombinatorialPolyhedron(SageObject): if self._f_vector: is_f_vector = True else: - # in this case we will calculate the f_vector while we're at it + # in this case we will compute the f_vector while we're at it is_f_vector = False if dim == 1: @@ -1686,7 +1722,28 @@ cdef class CombinatorialPolyhedron(SageObject): # Success, copy the data to ``CombinatorialPolyhedron``. if dual: - # We have actually calculated the ridges. + # We have actually computed the ridges. + sig_block() + self._nr_ridges = counter + self._ridges = edges + self._mem_tuple += (mem,) + sig_unblock() + else: + sig_block() + self._nr_edges = counter + self._edges = edges + self._mem_tuple += (mem,) + sig_unblock() + return 0 + + if dim == 0: + # There is no edge. + # Prevent calling the face iterator with an improper dimension. + counter = 0 + + # Success, copy the data to ``CombinatorialPolyhedron``. + if dual: + # We have actually computed the ridges. sig_block() self._nr_ridges = counter self._ridges = edges @@ -1701,14 +1758,15 @@ cdef class CombinatorialPolyhedron(SageObject): return 0 if is_f_vector: - # Only calculate the edges. + # Only compute the edges. if not dual: - face_iter.set_request_dimension(1) + face_iter = self._face_iter(dual, 1) else: - # :meth:`FaceIterator.set_request_dimension` - # requires the dimension of the original Polyhedron - face_iter.set_request_dimension(dim - 2) + # ``output_dimension`` in + # :meth:`FaceIterator.__init__` + # requires the dimension of the original polyhedron + face_iter = self._face_iter(dual, dim - 2) if self._nr_facets > 0 and dim > 0: # If not, there won't even be any edges. Prevent error message. @@ -1750,6 +1808,7 @@ cdef class CombinatorialPolyhedron(SageObject): self._mem_tuple += (mem,) sig_unblock() else: + face_iter = self._face_iter(dual, -2) # While doing the edges one might as well do the f-vector. f_vector = mem.calloc(dim + 2, sizeof(size_t)) f_vector[0] = 1 # This is not a proper face. @@ -1807,19 +1866,19 @@ cdef class CombinatorialPolyhedron(SageObject): self._mem_tuple += (mem,) sig_unblock() - cdef int _calculate_ridges(self, dual) except -1: + cdef int _compute_ridges(self, dual) except -1: r""" - Calculate the ridges of the Polyhedron. + Compute the ridges of the polyhedron. - If ``dual`` is ``True``, calculate the ridges of the polar. + If ``dual`` is ``True``, compute the ridges of the polar. See :meth:`edges` and :meth:`ridges`. """ if (self._edges is not NULL and dual) or (self._ridges is not NULL and not dual): - return 0 # There is no need to recalculate. + return 0 # There is no need to recompute. cdef MemoryAllocator mem = MemoryAllocator() - cdef FaceIterator face_iter = self._face_iter(dual) + cdef FaceIterator face_iter cdef size_t len_ridge_list = self._length_edges_list cdef int dim = self.dimension() @@ -1853,12 +1912,32 @@ cdef class CombinatorialPolyhedron(SageObject): sig_unblock() return 0 + if dim <= 1: + # There is no ridge, but face iterator expects a proper face dimension as input. + counter = 0 + + # Success, copy the data to ``CombinatorialPolyhedron``. + if not dual: + sig_block() + self._nr_ridges = counter + self._ridges = ridges + self._mem_tuple += (mem,) + sig_unblock() + else: + sig_block() + self._nr_edges = counter + self._edges = ridges + self._mem_tuple += (mem,) + sig_unblock() + return 0 + if dual: - # :meth:`FaceIterator.set_request_dimension` - # requires the dimension of the original Polyhedron - face_iter.set_request_dimension(1) + # ``output_dimension`` in + # :meth:`FaceIterator.__init` + # requires the dimension of the original polyhedron + face_iter = self._face_iter(dual, 1) else: - face_iter.set_request_dimension(dim - 2) + face_iter = self._face_iter(dual, dim -2) if self._nr_facets > 1 and dim > 0: # If not, there won't even be any ridges @@ -1902,14 +1981,14 @@ cdef class CombinatorialPolyhedron(SageObject): self._mem_tuple += (mem,) sig_unblock() - cdef int _calculate_face_lattice_incidences(self) except -1: + cdef int _compute_face_lattice_incidences(self) except -1: r""" - Calculate all incidences for the face lattice. + Compute all incidences for the face lattice. See :meth:`face_lattice`. """ if self._face_lattice_incidences: - return 1 # There is no need to recalculate the incidences. + return 1 # There is no need to recompute the incidences. cdef size_t len_incidence_list = self._length_edges_list cdef int dim = self.dimension() @@ -2009,9 +2088,9 @@ cdef class CombinatorialPolyhedron(SageObject): def _record_all_faces(self): r""" - Initialize :class:`ListOfAllFaces` for the Polyhedron. + Initialize :class:`ListOfAllFaces` for the polyhedron. - Record and sort all faces of the Polyhedron in that class. + Record and sort all faces of the polyhedron in that class. EXAMPLES:: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index f05c3aa1525..123029675f3 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -1,3 +1,71 @@ +r""" +Conversions + +This module provides conversions to class:`.list_of_faces.ListOfFaces` from +- an incidence matrix of a polyhedron or +- a tuple of facets (as tuple of vertices each). + +Also this module provides a conversion from the data of class:`list_of_faces.ListOfFaces`, +which is a Bit-vector representing incidences of a face, +to a list of entries which are incident. + +.. SEEALSO:: + + :mod:`.list_of_faces`, + :mod:`.face_iterator`, + :mod:`.base`. + +EXAMPLES: + +Obtain the facets of a polyhedron as :class:`.list_of_faces.ListOfFaces`:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import incidence_matrix_to_bit_repr_of_facets + sage: P = polytopes.simplex(4) + sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) + sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) + sage: face_list.compute_dimension() + 4 + +Obtain the vertices of a polyhedron as facet-incidences stored in +:class:`.list_of_faces.ListOfFaces`:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import incidence_matrix_to_bit_repr_of_vertices + sage: P = polytopes.associahedron(['A',4]) + sage: face_list = incidence_matrix_to_bit_repr_of_vertices(P.incidence_matrix()) + sage: face_list.compute_dimension() + 4 + +Obtain the facets of a polyhedron as :class:`.list_of_faces.ListOfFaces` from a facet list:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import facets_tuple_to_bit_repr_of_facets + sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) + sage: face_list = facets_tuple_to_bit_repr_of_facets(facets, 4) + +Likewise for the vertices as facet-incidences:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import facets_tuple_to_bit_repr_of_vertices + sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) + sage: face_list = facets_tuple_to_bit_repr_of_vertices(facets, 4) + +AUTHOR: + +- Jonathan Kliem (2019-04) +""" + +#***************************************************************************** +# Copyright (C) 2019 Jonathan Kliem +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from __future__ import absolute_import, division from sage.structure.element import is_Matrix @@ -20,7 +88,7 @@ cdef int vertex_list_to_bit_repr(tuple vertex_list, uint64_t *output, r""" Convert a vertex list into Bit-representation. Store it in ``output``. - The first bit represent the entry ``0`` and is set to one, iff ``0`` is in + The first bit represents the entry ``0`` and is set to one, iff ``0`` is in ``vertex_list``. The second bit represents ``1`` and so on. INPUT: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 89355f2d17c..1a89371e71c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -13,7 +13,7 @@ cdef class FaceIterator(SageObject): cdef int current_dimension # dimension of current face, dual dimension if ``dual`` cdef int dimension # dimension of the polyhedron cdef int nr_lines # ``_nr_lines`` of ``CombinatorialPolyhedron`` - cdef int request_dimension # only faces of this (dual?) dimension are considered + cdef int output_dimension # only faces of this (dual?) dimension are considered cdef int lowest_dimension # don't consider faces bewow this (dual?) dimension cdef MemoryAllocator _mem cdef tuple newfaces_lists # tuple to hold the ListOfFaces corresponding to maybe_newfaces diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 9c70f8a351d..eaa414f226b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,3 +1,163 @@ +r""" +FaceIterator + +This module provides a face iterator for polyhedra. + +This iterator in principle works on every graded lattice, where +every interval of length two has exactly 4 elements (diamond property). + +It also works on unbounded polyhedra, as those satisfy the diamond property, +except for intervals including the empty face. + +Terminology in this module: + +- Vertices -- ``[vertices, rays, lines]`` of the polyhedron. +- Facets -- facets of the polyhedron. +- Coatoms -- the faces from which all others are constructed in + the face iterator. This will be facets or vertices. + In non-dual mode, faces are constructed as + intersections of the facets. In dual mode, the are + constructed theoretically as joins of vertices. + The coatoms are reprsented as incidences with the + atoms they contain. +- Atoms -- facets or vertices depending on application of algorithm. + Atoms are reprsented as incidences of coatoms they + are contained in. + +- Vertex-Representation -- represents a face by a list of vertices it contains. +- Facet-Representation -- represents a face by a list of facets it is contained in. +- Bit-Representation -- represents incidences as ``uint64_t``-array, where + each Bit represents one incidences. There might + be trailing zeros, to fit alignment-requirements. + In most instances, faces are represented by the + Bit-representation, where each bit corresponds to + an atom. + +EXAMPLES: + +Construct a face iterator:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator \ + ....: import FaceIterator + sage: P = polytopes.octahedron() + sage: C = CombinatorialPolyhedron(P) + + sage: FaceIterator(C, False) + Iterator over the proper faces of a polyhedron of dimension 3 + sage: FaceIterator(C, False, output_dimension=2) + Iterator over the 2-faces of a polyhedron of dimension 3 + +Iterator in the non-dual mode starts with facets. +By default the dimension of the current face is given:: + + sage: it = FaceIterator(C, False) + sage: next(it) + 2 + +Iterator in the dual-mode start with vertices:: + + sage: it = FaceIterator(C, True) + sage: next(it) + 0 + +Obtain the vertex-representation:: + + sage: it = FaceIterator(C, False) + sage: next(it) + 2 + sage: it.vertex_repr() + (A vertex at (0, -1, 0), A vertex at (0, 0, -1), A vertex at (1, 0, 0)) + + sage: it.length_vertex_repr() + 3 + +Obtain the facet-representation:: + + sage: it = FaceIterator(C, True) + sage: next(it) + 0 + sage: it.facet_repr() + (An inequality (-1, -1, 1) x + 1 >= 0, + An inequality (-1, -1, -1) x + 1 >= 0, + An inequality (-1, 1, -1) x + 1 >= 0, + An inequality (-1, 1, 1) x + 1 >= 0) + sage: it.facet_repr(names=False) + (4, 5, 6, 7) + + sage: it.length_facet_repr() + 4 + +In non-dual mode one can ignore all faces contained in the current face:: + + sage: it = FaceIterator(C, False) + sage: next(it) + 2 + sage: it.facet_repr(names=False) + (7,) + sage: it.ignore_subfaces() + sage: [it.facet_repr(names=False) for _ in it] + [(6,), + (5,), + (4,), + (3,), + (2,), + (1,), + (0,), + (5, 6), + (1, 6), + (0, 1, 5, 6), + (4, 5), + (0, 5), + (0, 3, 4, 5), + (3, 4), + (2, 3), + (0, 3), + (0, 1, 2, 3), + (1, 2), + (0, 1)] + +In dual mode one can ignore all faces that contain the current face:: + + sage: it = FaceIterator(C, True) + sage: next(it) + 0 + sage: it.vertex_repr(names=False) + (5,) + sage: it.ignore_supfaces() + sage: [it.vertex_repr(names=False) for _ in it] + [(4,), + (3,), + (2,), + (1,), + (0,), + (3, 4), + (2, 4), + (0, 4), + (0, 3, 4), + (0, 2, 4), + (1, 3), + (0, 3), + (0, 1, 3), + (1, 2), + (0, 2), + (0, 1, 2), + (0, 1)] + +AUTHOR: + +- Jonathan Kliem (2019-04) +""" + +#***************************************************************************** +# Copyright (C) 2019 Jonathan Kliem +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from __future__ import absolute_import, division, print_function from sage.rings.integer cimport smallInteger @@ -11,7 +171,7 @@ cdef extern from "Python.h": cdef class FaceIterator(SageObject): r""" - A class to iterate over all faces of a Polyhedron. + A class to iterate over all faces of a polyhedron. Constructs all proper from the facets. In dual mode, constructs all proper faces from the vertices. Dual will be faster for less vertices than facets. @@ -19,8 +179,10 @@ cdef class FaceIterator(SageObject): INPUT: - ``C`` -- a :class:`CombinatorialPolyhedron` - - ``dual`` -- if True, then dual Polyhedron is used for iteration + - ``dual`` -- if ``True``, then dual polyhedron is used for iteration (only possible for bounded Polyhedra) + - ``output_dimension`` -- if not ``None``, then the FaceIterator will only yield + faces of this dimension .. SEEALSO:: @@ -49,7 +211,7 @@ cdef class FaceIterator(SageObject): 2 sage: it.facet_repr() (An inequality (-1, 0, 0) x + 1 >= 0, An inequality (-1, -1, 1) x + 2 >= 0) - sage: it.get_dimension() + sage: it.current_face_dimension() 1 Ignore faces the current face contains:: @@ -83,6 +245,19 @@ cdef class FaceIterator(SageObject): ... ValueError: only possible when not in dual mode + Construct a FaceIterator only yielding dimension `2` faces:: + + sage: P = polytopes.permutahedron(5) + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(dimension=2) + sage: counter = 0 + sage: for _ in it: counter += 1 + sage: print ('permutahedron(5) has', counter, + ....: 'faces of dimension 2') + permutahedron(5) has 150 faces of dimension 2 + sage: C.f_vector() + (1, 120, 240, 150, 30, 1) + ALGORITHM: The algorithm to visit all proper faces exactly once is roughly @@ -92,14 +267,14 @@ cdef class FaceIterator(SageObject): face_iterator(faces, []) def face_iterator(faces, visited_all): - # Visit all faces of a Polyhedron `P`, except those contained in + # Visit all faces of a polyhedron `P`, except those contained in # any of the visited all. # Assumes ``faces`` to be excactly those facets of `P` # that are not contained in any of the ``visited_all``. # Assumes ``visited_all`` to be some list of faces of - # a Polyhedron `P_2`, which contains `P` as one of its faces. + # a polyhedron `P_2`, which contains `P` as one of its faces. while len(facets) > 0: one_face = faces.pop() @@ -120,8 +295,6 @@ cdef class FaceIterator(SageObject): # Then, intersecting ``one_face`` with ``second_face`` gives # ``F``. ∎ - # Let ``maybe_newfaces2`` be the inclusion maximal faces of - # ``maybe_newfaces``. # If an element in ``maybe_newfaces`` is inclusion maximal # and not contained in any of the ``visited_all``, # it is a facet of ``one_face``. @@ -183,13 +356,26 @@ cdef class FaceIterator(SageObject): self.dimension = C.dimension() self.current_dimension = self.dimension -1 self.nr_lines = C._nr_lines - self.request_dimension = -2 self._mem = MemoryAllocator() - self.lowest_dimension = self.nr_lines + # We will not yield the empty face. # If there are `n` lines, than there # are no faces below dimension `n`. # The dimension of the level-sets in the face lattice jumps from `n` to `-1`. + self.lowest_dimension = self.nr_lines + + if output_dimension is not None: + if not output_dimension in range(0,self.dimension): + raise ValueError("``output_dimension`` must be the dimension of proper faces") + if self.dual: + # In dual mode, the dimensions are reversed. + self.output_dimension = self.dimension - 1 - output_dimension + else: + self.output_dimension = output_dimension + self.lowest_dimension = max(self.nr_lines, self.output_dimension) + else: + self.output_dimension = -2 + if dual: self.atoms = C.bitrep_facets self.coatoms = C.bitrep_vertices @@ -251,9 +437,21 @@ cdef class FaceIterator(SageObject): sage: P = polytopes.associahedron(['A',3]) sage: C = CombinatorialPolyhedron(P) sage: C.face_iter() - Iterator over the faces of a Polyhedron of dimension 3 + Iterator over the proper faces of a polyhedron of dimension 3 + + sage: C.face_iter(1) + Iterator over the 1-faces of a polyhedron of dimension 3 """ - return "Iterator over the faces of a Polyhedron of dimension %s"%self.dimension + if self.output_dimension != -2: + if self.dual: + # ouput_dimension is stored with respect to the dual + intended_dimension = self.dimension - 1 - self.output_dimension + else: + intended_dimension = self.output_dimension + output = "Iterator over the {}-faces".format(intended_dimension) + else: + output = "Iterator over the proper faces" + return output + " of a polyhedron of dimension {}".format(self.dimension) def __next__(self): r""" @@ -304,34 +502,7 @@ cdef class FaceIterator(SageObject): """ raise NotImplementedError - def set_request_dimension(self, dim): - r""" - Set the iterator to only yield faces of dimension ``dim``. - - EXAMPLES:: - - sage: P = polytopes.permutahedron(5) - sage: C = CombinatorialPolyhedron(P) - sage: it = C.face_iter() - sage: next(it) - 3 - sage: counter = 0 - sage: it.set_request_dimension(2) - sage: for _ in it: counter += 1 - sage: print ('permutahedron(5) has', counter, - ....: 'faces of dimension 2') - permutahedron(5) has 150 faces of dimension 2 - sage: C.f_vector() - (1, 120, 240, 150, 30, 1) - """ - if self.dual: - # In dual mode, the dimensions are reversed. - self.request_dimension = self.dimension - 1 - dim - else: - self.request_dimension = dim - self.lowest_dimension = max(self.nr_lines, self.request_dimension) - - def get_dimension(self): + def current_face_dimension(self): r""" Return the dimension of the current face. @@ -342,9 +513,9 @@ cdef class FaceIterator(SageObject): sage: it = C.face_iter() sage: next(it) 2 - sage: it.get_dimension() + sage: it.current_face_dimension() 2 - sage: all(d == it.get_dimension() for d in it) + sage: all(d == it.current_face_dimension() for d in it) True """ if unlikely(self.face is NULL): @@ -462,7 +633,7 @@ cdef class FaceIterator(SageObject): Return the facet-representation of the current face. The facet-representation consists of the facets - that contain the face and of the equalities of the Polyhedron. + that contain the face and of the equalities of the polyhedron. INPUT: @@ -650,7 +821,7 @@ cdef class FaceIterator(SageObject): r""" Set attribute ``face`` to the next face and return the dimension. - Will return the dimension of the Polyhedron on failure. + Will return the dimension of the polyhedron on failure. The function calls :meth:`FaceIterator.next_face_loop` until a new face is set or until the iterator is consumed. @@ -688,8 +859,8 @@ cdef class FaceIterator(SageObject): cdef size_t nr_faces = self.nr_newfaces[self.current_dimension] cdef size_t nr_visited_all = self.nr_visited_all[self.current_dimension] - if (self.request_dimension > -2) and (self.request_dimension != self.current_dimension): - # If only a specifice dimension was requested (i.e. ``self.request_dimension > 2``), + if (self.output_dimension > -2) and (self.output_dimension != self.current_dimension): + # If only a specific dimension was requested (i.e. ``self.output_dimension > -2``), # then we will not yield faces in other dimension. self.yet_to_visit = 0 @@ -761,7 +932,7 @@ cdef class FaceIterator(SageObject): cdef size_t length_atom_repr(self) except -1: r""" - Calculate the number of atoms in the current face by counting the + Compute the number of atoms in the current face by counting the number of set bits. """ if self.face: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_all_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_all_faces.pyx index fb2c034029e..68d3d6d04ba 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_all_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_all_faces.pyx @@ -1,3 +1,63 @@ +r""" +ListOfAllFaces + +This module provides a class that stores and sorts all faces of the polyhedron. + +:class:`.base.CombinatorialPolyhedron` implicitely uses this class to generate +the face lattice of a polyhedron. + +Terminology in this module: + +- Vertices -- ``[vertices, rays, lines]`` of the polyhedron. +- Facets -- facets of the polyhedron. +- Coatoms -- the faces from which all others are constructed in + the face iterator. This will be facets or vertices. + In non-dual mode, faces are constructed as + intersections of the facets. In dual mode, the are + constructed theoretically as joins of vertices. + The coatoms are reprsented as incidences with the + atoms they contain. +- Atoms -- facets or vertices depending on application of algorithm. + Atoms are reprsented as incidences of coatoms they + are contained in. + +- Vertex-Representation -- represents a face by a list of vertices it contains. +- Facet-Representation -- represents a face by a list of facets it is contained in. +- Bit-Representation -- represents incidences as ``uint64_t``-array, where + each Bit represents one incidences. There might + be trailing zeros, to fit alignment-requirements. + In most instances, faces are represented by the + Bit-representation, where each bit corresponds to + an atom. + +EXAMPLES:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_all_faces \ + ....: import ListOfAllFaces + sage: P = polytopes.octahedron() + sage: C = CombinatorialPolyhedron(P) + sage: all_faces = ListOfAllFaces(C) + +.. SEEALSO:: + + :mod:`.base`, + :class:`ListOfAllFaces`. + +AUTHOR: + +- Jonathan Kliem (2019-04) +""" + +#***************************************************************************** +# Copyright (C) 2019 Jonathan Kliem +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from __future__ import absolute_import, division, print_function from .conversions \ import facets_tuple_to_bit_repr_of_facets, \ @@ -21,7 +81,7 @@ cdef class ListOfAllFaces: are added and sorted (except coatoms). The incidences can be used to generate the ``face_lattice``. - Might generate the faces of the dual Polyhedron for speed. + Might generate the faces of the dual polyhedron for speed. INPUT: @@ -32,7 +92,7 @@ cdef class ListOfAllFaces: :meth:`CombinatorialPolyhedron._record_all_faces`, :meth:`CombinatorialPolyhedron._record_all_faces_helper`, :meth:`CombinatorialPolyhedron.face_lattice`, - :meth:`CombinatorialPolyhedron._calculate_face_lattice_incidences`. + :meth:`CombinatorialPolyhedron._compute_face_lattice_incidences`. EXAMPLES:: @@ -72,7 +132,7 @@ cdef class ListOfAllFaces: self.dual = True if C._unbounded: self.dual = False - cdef FaceIterator face_iter = C._face_iter(self.dual) + cdef FaceIterator face_iter = C._face_iter(self.dual, -2) self.face_length = face_iter.face_length self._V = C._V self._H = C._H @@ -98,7 +158,7 @@ cdef class ListOfAllFaces: # Initialize atoms, coatoms, ``atom_repr`` and ``coatom_repr``. if self.dimension == 0: - # In case of the 0-dimensional Polyhedron, we have to fix atoms and coatoms. + # In case of the 0-dimensional polyhedron, we have to fix atoms and coatoms. # So far this didn't matter, as we only iterated over proper faces. self.atoms = facets_tuple_to_bit_repr_of_vertices(((),), 1) self.coatoms = facets_tuple_to_bit_repr_of_facets(((),), 1) @@ -117,7 +177,7 @@ cdef class ListOfAllFaces: if self.dimension > -1: # the coatoms self.faces_mem += (self.coatoms,) - self.faces_mem += (ListOfFaces(1, nr_atoms),) # the full Polyhedron + self.faces_mem += (ListOfFaces(1, nr_atoms),) # the full polyhedron # Setting up a pointer to raw data of ``faces``: self.faces = self._mem.allocarray(self.dimension + 2, sizeof(uint64_t **)) @@ -294,7 +354,7 @@ cdef class ListOfAllFaces: Traceback (most recent call last): ... ValueError: cannot find a facet, as those are not sorted - sage: it.set_request_dimension(1) + sage: it = C.face_iter(dimension=1) sage: S = set(find_face_from_iterator(it, C) for _ in it) sage: S == set(range(36)) True @@ -380,8 +440,7 @@ cdef class ListOfAllFaces: ....: ''') sage: P = polytopes.permutahedron(4) sage: C = CombinatorialPolyhedron(P) - sage: it = C.face_iter() - sage: it.set_request_dimension(1) + sage: it = C.face_iter(dimension=1) sage: next(it) 1 sage: vertex_repr_via_all_faces_from_iterator(it, C, True) @@ -434,7 +493,7 @@ cdef class ListOfAllFaces: and index ``index``. The facet-representation consists of the facets - that contain the face and of the equalities of the Polyhedron. + that contain the face and of the equalities of the polyhedron. INPUT: @@ -463,8 +522,7 @@ cdef class ListOfAllFaces: ....: ''') sage: P = polytopes.permutahedron(4) sage: C = CombinatorialPolyhedron(P) - sage: it = C.face_iter() - sage: it.set_request_dimension(1) + sage: it = C.face_iter(dimension=1) sage: next(it) 1 sage: facet_repr_via_all_faces_from_iterator(it, C, True) @@ -587,7 +645,7 @@ cdef class ListOfAllFaces: This will enable :meth:`next_incidence` to give all such incidences. Currently only ``dimension_one == dimension_two + 1`` and incidences - with empty and full Polyhedron are implemented, which suffices for the + with empty and full polyhedron are implemented, which suffices for the face-lattice. """ cdef size_t i @@ -705,7 +763,7 @@ cdef class ListOfAllFaces: return is_it_equal if self.is_incidence_initialized == 2: - # the case where ``dimension_one`` is dimension of Polyhedron. + # the case where ``dimension_one`` is dimension of polyhedron. return self.next_trivial_incidence(one, two) if self.is_incidence_initialized == 3: @@ -717,7 +775,7 @@ cdef class ListOfAllFaces: cdef inline bint next_trivial_incidence(self, size_t *one, size_t *two): r""" - Handling the case where ``dimension_one`` is dimension of Polyhedron. + Handling the case where ``dimension_one`` is dimension of polyhedron. See :meth:`next_incidence`. """ diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd index 3742e022f98..037f48eb78c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd @@ -13,6 +13,6 @@ cdef class ListOfFaces: # It will be of "type" ``uint64_t[nr_faces][face_length]`` cdef uint64_t **data - cpdef int calculate_dimension(self) except -2 - cdef int calculate_dimension_loop(self, uint64_t **faces, size_t nr_faces, + cpdef int compute_dimension(self) except -2 + cdef int compute_dimension_loop(self, uint64_t **faces, size_t nr_faces, size_t face_length) except -2 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index fc5a62c4ed0..54a177b5b17 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -1,3 +1,80 @@ +r""" +ListOfFaces + +This module provides a class to store faces of a polyhedron in Bit-representation. + +This class allocates memory to store the faces in. +A face will be stored as vertex-incidences, where each Bit represents an incidence. +In :mod:`.conversions` there a methods to actually convert facets of a polyhedron +to bit-representations of vertices stored in :class:`ListOfFaces`. + +Moreover, :class:`ListOfFaces` calculates the dimension of a polyhedron, assuming the +faces are the facets of this polyhedron. + +Each face is stored over-aligned according to :meth:`.bit_vector_operations.chunktype`. + +EXAMPLES: + +Provide enough space to store `20` faces as incidences to `60` vertices:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ + ....: import ListOfFaces + sage: face_list = ListOfFaces(20, 60) + +Obtain the facets of a polyhedron:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import incidence_matrix_to_bit_repr_of_facets + sage: P = polytopes.cube() + sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) + sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) + sage: face_list.compute_dimension() + 3 + +Obtain the vertices of a polyhedron as facet-incidences:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import incidence_matrix_to_bit_repr_of_vertices + sage: P = polytopes.associahedron(['A',3]) + sage: face_list = incidence_matrix_to_bit_repr_of_vertices(P.incidence_matrix()) + sage: face_list.compute_dimension() + 3 + +Obtain the facets of a polyhedron as :class:`ListOfFaces` from a facet list:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import facets_tuple_to_bit_repr_of_facets + sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) + sage: face_list = facets_tuple_to_bit_repr_of_facets(facets, 4) + +Likewise for the vertices as facet-incidences:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import facets_tuple_to_bit_repr_of_vertices + sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) + sage: face_list = facets_tuple_to_bit_repr_of_vertices(facets, 4) + +.. SEEALSO:: + + :mod:`.base`, + :mod:`.face_iterator`, + :mod:`.conversions`, + :mod:`.list_of_all_faces`. + +AUTHOR: + +- Jonathan Kliem (2019-04) +""" + +#***************************************************************************** +# Copyright (C) 2019 Jonathan Kliem +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** from __future__ import absolute_import, division from sage.structure.element import is_Matrix @@ -16,7 +93,7 @@ cdef class ListOfFaces: INPUT: - ``nr_faces`` -- the number of faces to be stored - - ``nr_vertices`` -- the total number of vertices of the Polyhedron + - ``nr_vertices`` -- the total number of vertices of the polyhedron .. SEEALSO:: @@ -90,9 +167,11 @@ cdef class ListOfFaces: self.data[i] = \ self._mem.aligned_malloc(chunksize//8, self.face_length*8) - cpdef int calculate_dimension(self) except -2: + cpdef int compute_dimension(self) except -2: r""" - Calculate the dimension of a Polyhedron by its facets. + Compute the dimension of a polyhedron by its facets. + + This assumes that ``self`` is the list of facets of a polyhedron. EXAMPLES:: @@ -105,28 +184,28 @@ cdef class ListOfFaces: ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) sage: facets = facets_tuple_to_bit_repr_of_facets(bi_pyr, 6) sage: vertices = facets_tuple_to_bit_repr_of_vertices(bi_pyr, 6) - sage: facets.calculate_dimension() + sage: facets.compute_dimension() 3 - sage: vertices.calculate_dimension() + sage: vertices.compute_dimension() 3 ALGORITHM: This is done by iteration: - Calculates the facets of one of the facets (i.e. the ridges contained in - one of the facets). Then calculates the dimension of the facet, by + Computes the facets of one of the facets (i.e. the ridges contained in + one of the facets). Then computes the dimension of the facet, by considering its facets. Repeats until a face has only one facet. Usually this is a vertex. However, in the unbounded case, this might be different. The face with only one facet might be a ray or a line. So the correct dimension of a - Polyhedron with one facet is the number of ``[lines, rays, vertices]`` + polyhedron with one facet is the number of ``[lines, rays, vertices]`` that the facet contains. Hence, we know the dimension of a face, which has only one facet and - iteratively we know the dimension of entire Polyhedron we started with. + iteratively we know the dimension of entire polyhedron we started from. TESTS:: @@ -146,19 +225,19 @@ cdef class ListOfFaces: ....: d1 = P.dimension() ....: if d1 == 0: ....: continue - ....: d2 = facets.calculate_dimension() - ....: d3 = vertices.calculate_dimension() + ....: d2 = facets.compute_dimension() + ....: d3 = vertices.compute_dimension() ....: if not d1 == d2 == d3: ....: print('calculation_dimension() seems to be incorrect') """ if self.nr_faces == 0: raise TypeError("at least one face needed") - return self.calculate_dimension_loop(self.data, self.nr_faces, self.face_length) + return self.compute_dimension_loop(self.data, self.nr_faces, self.face_length) - cdef int calculate_dimension_loop(self, uint64_t **faces, size_t nr_faces, + cdef int compute_dimension_loop(self, uint64_t **faces, size_t nr_faces, size_t face_length) except -2: r""" - Calculate the dimension of a Polyhedron by its facets. + Compute the dimension of a polyhedron by its facets. INPUT: @@ -168,18 +247,18 @@ cdef class ListOfFaces: OUTPUT: - - dimension of the Polyhedron + - dimension of the polyhedron .. SEEALSO:: - :meth:`calculate_dimension` + :meth:`compute_dimension` """ if nr_faces == 0: - raise TypeError("wrong usage of ``calculate_dimension_loop``,\n" + + raise TypeError("wrong usage of ``compute_dimension_loop``,\n" + "at least one face needed.") if nr_faces == 1: - # We expect the face to be the empty Polyhedron. + # We expect the face to be the empty polyhedron. # Possibly it contains more than one vertex/rays/lines. # The dimension of a polyhedron with this face as only facet is # the number of atoms it contains. @@ -202,6 +281,6 @@ cdef class ListOfFaces: newfaces, NULL, 0, face_length) sig_off() - # Calculate the dimension of the polyhedron, + # compute the dimension of the polyhedron, # by calculating dimension of one of its faces. - return self.calculate_dimension_loop(newfaces, new_nr_faces, face_length) + 1 + return self.compute_dimension_loop(newfaces, new_nr_faces, face_length) + 1