5

There are two set of points

p={{5,2,0,1},{4,3,0,1},{4,2,0,2},{4,1,1,2},{3,4,1,0},{3,1,3,1},{3,1,1,3},{2,5,1,0},{2,4,2,0},{2,1,4,1},{2,0,4,2},{1,4,2,1},{1,3,3,1},{1,3,1,3},{1,2,1,4},{1,0,5,2},{1,0,4,3},{0,2,2,4},{0,1,3,4},{0,1,2,5}};
q={{8,0,0,0},{0,8,0,0},{0,0,8,0},{0,0,0,8},{7,1,0,0},{7,0,1,0},{7,0,0,1},{1,7,0,0},{1,0,7,0},{1,0,0,7},{0,7,1,0},{0,7,0,1},{0,1,7,0},{0,1,0,7},{0,0,7,1},{0,0,1,7},{6,2,0,0},{6,0,2,0},{6,0,0,2},{2,6,0,0},{2,0,6,0},{2,0,0,6},{0,6,2,0},{0,6,0,2},{0,2,6,0},{0,2,0,6},{0,0,6,2},{0,0,2,6},{6,1,1,0},{6,1,0,1},{6,0,1,1},{1,6,1,0},{1,6,0,1},{1,1,6,0},{1,1,0,6},{1,0,6,1},{1,0,1,6},{0,6,1,1},{0,1,6,1},{0,1,1,6},{5,3,0,0},{5,0,3,0},{5,0,0,3},{3,5,0,0},{3,0,5,0},{3,0,0,5},{0,5,3,0},{0,5,0,3},{0,3,5,0},{0,3,0,5},{0,0,5,3},{0,0,3,5},{5,2,1,0},{5,2,0,1},{5,1,2,0},{5,1,0,2},{5,0,2,1},{5,0,1,2},{2,5,1,0},{2,5,0,1},{2,1,5,0},{2,1,0,5},{2,0,5,1},{2,0,1,5},{1,5,2,0},{1,5,0,2},{1,2,5,0},{1,2,0,5},{1,0,5,2},{1,0,2,5},{0,5,2,1},{0,5,1,2},{0,2,5,1},{0,2,1,5},{0,1,5,2},{0,1,2,5},{5,1,1,1},{1,5,1,1},{1,1,5,1},{1,1,1,5},{4,4,0,0},{4,0,4,0},{4,0,0,4},{0,4,4,0},{0,4,0,4},{0,0,4,4},{4,3,1,0},{4,3,0,1},{4,1,3,0},{4,1,0,3},{4,0,3,1},{4,0,1,3},{3,4,1,0},{3,4,0,1},{3,1,4,0},{3,1,0,4},{3,0,4,1},{3,0,1,4},{1,4,3,0},{1,4,0,3},{1,3,4,0},{1,3,0,4},{1,0,4,3},{1,0,3,4},{0,4,3,1},{0,4,1,3},{0,3,4,1},{0,3,1,4},{0,1,4,3},{0,1,3,4},{4,2,2,0},{4,2,0,2},{4,0,2,2},{2,4,2,0},{2,4,0,2},{2,2,4,0},{2,2,0,4},{2,0,4,2},{2,0,2,4},{0,4,2,2},{0,2,4,2},{0,2,2,4},{4,2,1,1},{4,1,2,1},{4,1,1,2},{2,4,1,1},{2,1,4,1},{2,1,1,4},{1,4,2,1},{1,4,1,2},{1,2,4,1},{1,2,1,4},{1,1,4,2},{1,1,2,4},{3,3,2,0},{3,3,0,2},{3,2,3,0},{3,2,0,3},{3,0,3,2},{3,0,2,3},{2,3,3,0},{2,3,0,3},{2,0,3,3},{0,3,3,2},{0,3,2,3},{0,2,3,3},{3,3,1,1},{3,1,3,1},{3,1,1,3},{1,3,3,1},{1,3,1,3},{1,1,3,3},{3,2,2,1},{3,2,1,2},{3,1,2,2},{2,3,2,1},{2,3,1,2},{2,2,3,1},{2,2,1,3},{2,1,3,2},{2,1,2,3},{1,3,2,2},{1,2,3,2},{1,2,2,3},{2,2,2,2}}

Note that the sum of coordinates of points in a point set is 8. How to determine which points in q are in the convex hull of p?

flinty
  • 25,147
  • 2
  • 20
  • 86
lapcal
  • 531
  • 2
  • 7

2 Answers2

8

ConvexHullMesh and ConvexHullRegion do not work for higher dimensions beyond 3 yet. However, you can find a Convex Hull using LinearOptimization. This is inefficient compared to other algorithms, but simpler to write in Mathematica, and should be fast enough for small numbers of points like your problem:

(** Checks if the ith point in pts is an extreme point.
 - This minimizes λ[i] subject to convexity constraints.
 - If a point is extreme then it's on the edge of the hull.
 - λ[i] will be 1 if it's an extreme point
 - otherwise the ith point is a convex combination
 - We use the Simplex method because higher precision may be desired
 - The x_ /; x == 1 is so the Count uses numerical equality, see docs PossibleIssues section. **) 
isextreme[pts_, i_] := 
 With[{n = Length@pts, Λ = Array[λ, Length@pts]}, 
  Count[Values@LinearOptimization[λ[i], {
       Λ . pts == pts[[i]], 
       Total[Λ] == 1, Λ \[VectorGreaterEqual] 
       ConstantArray[0, n]
     }, Λ, Method -> "Simplex"], x_ /; x == 1] == 1]

(** These messages will be suppressed:

  • nsolc: no solutions
  • evcnstr2: Mathematica cannot satisfy the constraints, e.g due to precision issues. **)

msgs = {LinearOptimization::nsolc, LinearOptimization::evcnstr2};

(** Checks if a point p is inside the convex hull.

  • The objective is constant since we only care about constraints.
  • If it is in or on the hull, then the contraints are satisfied.
  • If the msgs are raised, then the point is outside the hull or the feasible region cannot be determined. **)

isinside[hull_, p_] := With[{n = Length@hull, Λ = Array[λ, Length@hull]}, Quiet[Check[ LinearOptimization[1, { Λ . hull == p, Total[Λ] == 1, Λ [VectorGreaterEqual] ConstantArray[0, n] }, Λ, Method -> "Simplex"]; True, False, Evaluate@msgs], Evaluate@msgs]]

(** Finds all the extreme points **) lpConvexHull[pts_] := Sort[Pick[pts, MapIndexed[isextreme[pts, #2[[1]]] &, pts]]]

(** Take our convex hull and determine which q are inside it **) cvxh = lpConvexHull[p]; qinside = Select[q, isinside[cvxh, #] &]

(** {{5, 2, 0, 1}, {2, 5, 1, 0}, {1, 0, 5, 2}, {0, 1, 2, 5}, {4, 3, 0, 1}, {3, 4, 1, 0}, {1, 0, 4, 3}, {0, 1, 3, 4}, {4, 2, 0, 2}, {2, 4, 2, 0}, {2, 0, 4, 2}, {0, 2, 2, 4}, {4, 2, 1, 1}, {4, 1, 1, 2}, {2, 4, 1, 1}, {2, 1, 4, 1}, {1, 4, 2, 1}, {1, 2, 1, 4}, {1, 1, 4, 2}, {1, 1, 2, 4}, {3, 3, 1, 1}, {3, 1, 3, 1}, {3, 1, 1, 3}, {1, 3, 3, 1}, {1, 3, 1, 3}, {1, 1, 3, 3}, {3, 2, 2, 1}, {3, 2, 1, 2}, {3, 1, 2, 2}, {2, 3, 2, 1}, {2, 3, 1, 2}, {2, 2, 3, 1}, {2, 2, 1, 3}, {2, 1, 3, 2}, {2, 1, 2, 3}, {1, 3, 2, 2}, {1, 2, 3, 2}, {1, 2, 2, 3}, {2, 2, 2, 2}} **)

flinty
  • 25,147
  • 2
  • 20
  • 86
  • 1
    -1. A good code is a commented code. – user64494 May 02 '23 at 18:46
  • What do isextreme[pts_, i_] and msgs and isinside[hull_, p_] do? – user64494 May 02 '23 at 18:53
  • Thank you for your edit. I still understand neither LinearOptimization::nsolc nor LinearOptimization::evcnstr2}. – user64494 May 02 '23 at 19:48
  • 3
    @user64494 As an experienced professional software engineer, I disagree very strongly with your general point about sic '[a] good code'. Comments can rot, are not parsed, and they are rarely as informative as just choosing good names for identifiers. This is also an academic example I am providing for free with less than 9 hours to work on. Please refer to a textbook on operations research if you do not understand the setup or terms like extreme points and feasible region. – flinty May 02 '23 at 19:57
  • Can you kindly explain msgs = {LinearOptimization::nsolc, LinearOptimization::evcnstr2};? TIA. I prefer arguments and formulas over emotional words. ` – user64494 May 02 '23 at 20:03
  • @user64494 This line is a list of error messages. LinearOptimization can fail to find a solution if the point is outside the feasible region. That's the LinearOptimization::nsolc. Also LinearOptimization::evcnstr2 is another message it can raise, caused by a failure to meet the constraints due to precision issues. Further down you'll see a Check where if any of the msgs are raised during the LinearOptimization, then it will return False (unable to determine if the point is inside the hull), otherwise I return True (it's inside the hull or on the boundary). – flinty May 02 '23 at 20:15
  • flinty (@ does not work.): Thank you for your explanation. – user64494 May 02 '23 at 20:21
  • @flinty Thanks very much. I have a more efficient method using ConvexHullRegion and Delete, Pick[q,RegionMember@ConvexHullRegion[Delete[#,1]&/@p]/@(Delete[#,1]&/@q)] , the result is same as yours. But if I use Delete[#,2]& and Delete[#,4]& , the result is incorrect. So I can‘t guarantee the correctness of this method. – lapcal May 03 '23 at 03:29
  • @lapcal This is not correct. You cannot delete those dimensions. If you were able to find the projection of your points onto 3D or 2D then you could use ConvexHullRegion. However, NullSpace[p] is empty so there isn't some lower dimensional subspace you can project your points onto without losing information. – flinty May 03 '23 at 10:26
  • @lapcal I suppose you got lucky here and there weren't any additional points that fell into the 'shadow' of the hull while not being in the full dimensional hull. In general though, this will not work. For example consider a 3D pyramid hull - any points inside clearly end up in a triangle shadow on the 2D plane, but a point just above and outside the pyramid would too, even though it's not part of the full 3D hull – flinty May 03 '23 at 10:53
-2

Another way is as follows. Each point of the convex hull of p={{5,2,0,1},{4,3,0,1},{4,2,0,2},{4,1,1,2},{3,4,1,0},{3,1,3,1},{3,1,1,3},{2,5,1,0},{2,4,2,0},{2,1,4,1},{2,0,4,2},{1,4,2,1},{1,3,3,1},{1,3,1,3},{1,2,1,4},{1,0,5,2},{1,0,4,3},{0,2,2,4},{0,1,3,4},{0,1,2,5}} is of the form Sum[x[j]*p[[j]], {j, 1, Dimensions[p][[1]]}], where Sum[x[j], {j, 1,Dimensions[p][[1]]}] == 1 && Table[x[j], {j, 1, Dimensions[p][[1]]}] >= 0, i.e. a linear combination of the points of p (more exactly, the vectors from the origin to these points).

Now we minimize the square of the distance between a point of q, say {8, 0, 0, 0} or/and {2, 5, 1, 0}, and the convex hull of p by

Minimize[{Norm[Sum[x[j]*p[[j]], {j, 1, 20}] - {8, 0, 0, 0}]^2, 
Sum[x[j], {j, 1, 20}] == 1 && Table[x[j], {j, 1, 20}] >= 0}, 
Table[x[j], {j, 1, 20}]]

{6, {x[1] -> 0, x[2] -> 0, x[3] -> 0, x[4] -> 0, x[5] -> 0, x[6] -> 0, x[7] -> 0, x[8] -> 1, x[9] -> 0, x[10] -> 0, x[11] -> 0, x[12] -> 0, x[13] -> 0, x[14] -> 0, x[15] -> 0, x[16] -> 0, x[17] -> 0, x[18] -> 0, x[19] -> 0, x[20] -> 0}}

We see the former is outside the convex hull.

Minimize[{Norm[Sum[x[j]*p[[j]], {j, 1, 20}] - {2, 5, 1, 0}]^2, 
Sum[x[j], {j, 1, 20}] == 1 && Table[x[j], {j, 1, 20}] >= 0}, 
Table[x[j], {j, 1, 20}]]

{0, {x[1] -> 0, x[2] -> 0, x[3] -> 0, x[4] -> 0, x[5] -> 0, x[6] -> 0, x[7] -> 0, x[8] -> 1, x[9] -> 0, x[10] -> 0, x[11] -> 0, x[12] -> 0, x[13] -> 0, x[14] -> 0, x[15] -> 0, x[16] -> 0, x[17] -> 0, x[18] -> 0, x[19] -> 0, x[20] -> 0}}

We conclude the latter belongs to the convex hull.

user64494
  • 26,149
  • 4
  • 27
  • 56
  • 1
    Minus votes without any comment/explanation are not a good practice. – user64494 May 03 '23 at 04:28
  • You can use SquaredEuclideanDistance[..., ...] instead of doing Norm[...]^2. One downside to this method is you've turned a linear problem into a quadratic one, which is not a disaster since there are fast solvers and a nice gradient for Minimize. I would have written it like this: With[{vars = Array[x, Length@p], n = Length@p}, Minimize[{SquaredEuclideanDistance[vars . p, {8, 0, 0, 0}], Total[vars] == 1 && (And @@ Thread[vars >= 0])}, vars]] - also you need to handle the potential error cases, and show how to produce the list of q that's inside the hull. – flinty May 03 '23 at 11:23
  • @user64494, actually, explaining downvotes is generally not recommended. (I did not downvote, though.) – Domen May 05 '23 at 13:06