Skip to content

Commit

Permalink
Trac #17215: Normal cone of faces of polyhedra
Browse files Browse the repository at this point in the history
A point v on a convex polyhedron P is a vertex of P if and only if there
exists an affine hyperplane in the linear span of P which intersects P
only in v. Knowing such affine hyperplane is a good certificate for v
being a vertex.

This ticket implements the method `normal_cone` for faces of polyhedra,
consisting of all the directions of the normals to supporting
hyperplanes of the specified face.

URL: https://trac.sagemath.org/17215
Reported by: darij
Ticket author(s): Jean-Philippe Labbé
Reviewer(s): Jonathan Kliem
  • Loading branch information
Release Manager committed Feb 13, 2020
2 parents 6dbca5f + 7278f23 commit 309e370
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 2 deletions.
3 changes: 1 addition & 2 deletions src/sage/geometry/polyhedron/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4797,8 +4797,7 @@ def face_truncation(self, face, linear_coefficients=None, cut_frac=None):

B = - normal_vector * (face_vertices[0].vector())

linear_evaluation = set(-normal_vector * (v.vector()) for v in
self.vertices())
linear_evaluation = set(-normal_vector * (v.vector()) for v in self.vertices())

if B == max(linear_evaluation):
C = max(linear_evaluation.difference(set([B])))
Expand Down
80 changes: 80 additions & 0 deletions src/sage/geometry/polyhedron/face.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,86 @@ def as_polyhedron(self):
Vrep = (self.vertices(), self.rays(), self.lines())
return P.__class__(parent, Vrep, None)

@cached_method
def normal_cone(self, direction='outer'):
"""
Return the pointed polyhedral cone consisting of normal vectors to
hyperplanes supporting ``self``.
INPUT:
- ``direction`` -- string (default: ``'outer'``), the direction in
which to consider the normals. The other allowed option is
``'inner'``.
OUTPUT:
A polyhedron.
EXAMPLES::
sage: p = Polyhedron(vertices = [[1,2],[2,1],[-2,2],[-2,-2],[2,-2]])
sage: for v in p.face_generator(0):
....: vect = v.vertices()[0].vector()
....: nc = v.normal_cone().rays_list()
....: print("{} has outer normal cone spanned by {}".format(vect,nc))
....:
(2, 1) has outer normal cone spanned by [[1, 0], [1, 1]]
(1, 2) has outer normal cone spanned by [[0, 1], [1, 1]]
(2, -2) has outer normal cone spanned by [[0, -1], [1, 0]]
(-2, -2) has outer normal cone spanned by [[-1, 0], [0, -1]]
(-2, 2) has outer normal cone spanned by [[-1, 0], [0, 1]]
sage: for v in p.face_generator(0):
....: vect = v.vertices()[0].vector()
....: nc = v.normal_cone(direction='inner').rays_list()
....: print("{} has inner normal cone spanned by {}".format(vect,nc))
....:
(2, 1) has inner normal cone spanned by [[-1, -1], [-1, 0]]
(1, 2) has inner normal cone spanned by [[-1, -1], [0, -1]]
(2, -2) has inner normal cone spanned by [[-1, 0], [0, 1]]
(-2, -2) has inner normal cone spanned by [[0, 1], [1, 0]]
(-2, 2) has inner normal cone spanned by [[0, -1], [1, 0]]
The function works for polytopes that are not full-dimensional::
sage: p = polytopes.permutahedron(3)
sage: f1 = p.faces(0)[0]
sage: f2 = p.faces(1)[0]
sage: f3 = p.faces(2)[0]
sage: f1.normal_cone()
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line
sage: f2.normal_cone()
A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line
sage: f3.normal_cone()
A 1-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex and 1 line
Normal cones are only defined for non-empty faces::
sage: f0 = p.faces(-1)[0]
sage: f0.normal_cone()
Traceback (most recent call last):
...
ValueError: the empty face does not have a normal cone
"""
if self.dim() == -1:
raise ValueError("the empty face does not have a normal cone")
elif direction not in ['outer','inner']:
raise ValueError("the direction should be either 'outer' or 'inner'")
rays = []
lines = []
for facet in self.ambient_Hrepresentation():
if facet.is_equation():
lines += [facet.A()]
elif direction == 'outer':
rays += [-facet.A()]
else: # 'inner'
rays += [facet.A()]
parent = self.polyhedron().parent()
origin = self.polyhedron().ambient_space().zero()
return parent.element_class(parent, [[origin], rays, lines], None)


def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face):
r"""
Convert a combinatorial face to a face of a polyhedron.
Expand Down

0 comments on commit 309e370

Please sign in to comment.