Convex Polyhedra and Constrained Convex Hulls
Suppose we have a set of points
A convex combination of these points is a weighted sum with non-negative weights satisfying . The convex hull of is the set of all convex combinations of points in . Gathering the points in into the columns of the matrix , we can write the convex hull as the set
where is a vector of ones, is the vector of weights, and the inequality is taken elementwise.
In this post, we explore how to put additional constraints on the weights in and what the resulting set looks like.
Polyhedral Double Description
Let’s begin with some background on polyhedral geometry. A polyhedron is a geometric shape with flat sides (called faces). In this post we will assume we are talking about convex polyhedra; a polyhedron is convex when the line between any two points in the polyhedron is also completely contained in it. A bounded polyhedron is called a polytope and a two-dimensional polyhedron is called a polygon, some examples of which are shown in Figure 1 below.
Figure 1: Examples of two (convex) polygons, each of which can be defined as the convex hull of its vertices.
By the Minkowski-Weyl theorem, any polyhedron can be equivalently represented in two different ways, together known as the double description of . These two representations are:
Vertex Representation
The vertex representation (V-rep) can be written as
where each column of is a vertex and each column of is a ray (together these are referred to as generators). When , is just a convex hull like . When , instead reduces to a conical hull; however, we will only consider cases with in this post.
Halfspace Representation
The halfspace representation (H-rep) can be written as
which is a finite intersection of halfspaces. Equality constraints of the form , which serve to restrict to the polyhedron to a lower-dimensional subspace of can also be included by equivalently using two inequalities:
Converting from H-rep to V-rep is called vertex enumeration, while converting from V-rep to H-rep is called facet enumeration. No provably polynomial-time algorithms are known for these conversions, but nevertheless they can be computed fairly quickly when the dimension and number of generators and faces is small, using a library like cddlib or its Python bindings pycddlib.
You can read more about polyhedral double description here and see some examples of its application in robotics in this paper as well as this more recent paper of mine.
Computing Constrained Convex Hulls
Now that we’re equipped with some background on polyhedra, you may notice that the set of weights we used to define the convex hull is itself the H-rep of the polyhedron
This particular polyhedron is known as a simplex and is a special case of the general form with
where is the identity matrix. To incorporate additional constraints , where , , and (without these latter two conditions, the constraints could be inconsistent because is impossible to realize if or ), we add them to and to obtain
Converting this H-rep to V-rep, we get the set
with some matrix (note is no longer a simplex). Substituting into , we get the constrained convex hull
which is another V-rep polyhedron with weights and vertices given by the columns of .
Visualizing Constrained Convex Hulls
Now that we can compute the vertices of a constrained convex hull, we can plot it. Figure 2 below shows the constrained convex hulls for different values of and . You can play with this yourself using the Python code here.
Figure 2: Examples of constrained convex hulls. The original convex hull is shown in blue, while each of the red polygons is the convex hull with different bounds applied. From top to bottom we have bounds: , , and .
As we can see, the lower bound has the effect of “pulling” the hull in from the faces of the original polytope, while the upper bound pulls the hull away from the vertices. We only consider bounds in this post, but in general we can add any desired affine constraints to .
Analytical Solutions
It turns out that when have only lower or upper bounds (not both) then we can calculate the vertices of the constrained convex hulls analytically (that is, without converting between V-rep and H-rep).
Lower Bounds
Let us first consider lower bounds . The th vertex , which corresponds to weight , pulls the convex hull toward it from each other vertex by a factor of . An example with the shape from Figure 2 with a lower bound on only one of the vertex weights and no (additional) upper bounds is shown in Figure 3 below.
Figure 3: The constrained convex hull (red) of the polygon from Figure 2 (blue) with bounds , , and . The vertex corresponding to the non-zero lower bound is on the far left side. The lower bound has the effect of “pulling” each of the other vertices toward the bounded vertex, proportionally to the value of the bound. To see this, a line has been drawn between the bounded vertex and each other vertex, with a fraction proportional to coloured yellow and the remainder, proportional to , in green (entire figure shown on gray background for better contrast).
Each vertex of the constrained convex hull depends on the bounds at all vertices of the original shape. To compute it, we have the formula
Implemented in Python code, it looks like
# V is the array of original vertices, one per row, shape (n, d) (yes, this is
# the transpose of the the definition of V in eq. (1) - sorry)
# l is the array of lower bounds, shape (n,)
V_new = [] # vertices of the lower-bounded convex hull
for i in range(n):
v_new = V[i, :] + sum([l[j] * (V[j, :] - V[i, :]) for j in range(n)])
V_new.append(v_new)Upper Bounds
The upper bounds are a bit different. Each lower bound affected all other vertices, while each upper bound only affects the current vertex (i.e., the one corresponding to the bound), and this effect is a function of only the adjacent (i.e., neighbouring) vertices. Furthermore, unlike the lower-bounded case, the upper bound often results in a constrained convex hull with a different number of vertices than the original polytope. An example with the shape from Figure 2 with only a single upper bound on the vertex weights and no (additional) lower bounds is shown in Figure 4 below.
Figure 4: The constrained convex hull (red) of the polygon from Figure 2 (blue) with bounds and with . The vertex corresponding to the non-unity upper bound is on the far left side. The upper bound has the effect of pulling the hull toward the neighbouring vertices, proportionally to , creating a new face (dashed line). To see this, a line has been drawn between the bounded vertex and its two neighbours, with a fraction proportional to coloured yellow and the remainder, proportional to , in green.
Let be the set of vertices of the constrained convex hull, initially empty. Let be the set of indices of the vertices adjacent to vertex . For each vertex and each of its adjacent vertices , we compute
and add it to the set of new vertices:
Once we’ve gone through all vertices and their neighbours, we are left with the complete set of vertices of the upper-bounded convex hull. In Python, we have
# V is the array of original vertices, one per row, shape (n, d)
# u is the array of upper bounds, shape (n,)
# note I assume we have a function adj(V, i) that returns the indices of
# vertices adjacent to V[i, :], but this can be computed fairly efficiently (in
# my actual code I use pycddlib, which uses linear programming to compute
# adjacency)
V_new = [] # vertices of the upper-bounded convex hull
for i in range(n):
for j in adj(V, i):
v_new = V[j, :] + u[i] * (V[i, :] - V[j, :])
V_new.append(v_new)Note that this approach for computing the upper-bounded convex hull only works when for all and . Otherwise, the new faces added for each upper bound can intersect with each other, which complicates the solution considerably.
Both Lower and Upper Bounds
If we have both lower and upper bounds, we can independently compute the resulting constrained convex hulls using the analytical formulas above. To combine them into a single constrained convex hull, we need to take their intersection—unfortunately, I am not aware of an analytical solution for this in the vertex representation. Instead, we must again turn to polyhedral double description.
Let and be the sets of vertices of the lower- and upper-bounded convex hulls, respectively. Then:
- Convert both of these V-rep polytopes to the H-reps and .
- Stack the H-reps to combine them into the single polytope where
- Convert the combined H-rep polytope back into V-rep if desired (e.g., for plotting).
Uniform Padding
Finally, notice that the width of the padding between the boundary of the constrained convex hull and that of the original shape produced by the lower bound in Figure 2 is not uniform. If you do want uniform padding, do the following:
- Convert the polytope to the H-rep .
- For each row of and corresponding element of , normalize and subtract the desired padding width :
- Convert back to the V-rep for plotting.
The normalization in the second step makes each row a unit vector without changing the polytope defined by the inequalities. This ensures each inequality has the same scale, so that subtracting from each results in the same width of padding for each face. An example of uniform padding is shown in Figure 5 below.
Figure 5: The same polygon from Figure 2 inset with uniform padding of width pixels.
Thanks to Abhishek Goudar for reviewing a draft of this post.