2 #include <barvinok/options.h>
3 #include <barvinok/sample.h>
4 #include <barvinok/util.h>
11 Polyhedron
*P
; /* Original polyhedron or cone */
12 Polyhedron
*init
; /* Initial (under)approximation of integer hull */
14 Matrix
*F
; /* Set of already computed facets */
15 int n_F
; /* The number of computed facets */
17 /* Computes a lower bound for the objective function obj over
18 * the integer hull, possibly exploiting the already computed
19 * facets of the integer hull given in hull->F.
21 void (*set_lower_bound
)(struct integer_hull
*hull
, Value
*obj
,
22 Value
*lower
, struct barvinok_options
*options
);
25 static int matrix_add(Matrix
*M
, int n
, Value
*v
)
28 Matrix_Extend(M
, 3*(M
->NbRows
+10)/2);
29 Vector_Copy(v
, M
->p
[n
], M
->NbColumns
);
33 /* Select "best" constraint from P over which to optimize,
34 * where P is the current approximation of the integer hull
35 * and where a constraint is better if the difference between
36 * the value at the constraint and the lower bound (based on
37 * the original polytope (the linear relaxation) is smaller.
39 static int select_best(struct integer_hull
*hull
,
41 Value
*min
, Value
*max
,
42 struct barvinok_options
*options
)
45 Vector
*lower
= Vector_Alloc(P
->NbConstraints
);
46 Vector
*upper
= Vector_Alloc(P
->NbConstraints
);
47 Vector
*distances
= Vector_Alloc(P
->NbConstraints
);
52 for (i
= 0; i
< P
->NbConstraints
; ++i
) {
53 if (First_Non_Zero(P
->Constraint
[i
]+1, P
->Dimension
) == -1)
55 for (j
= 0; j
< hull
->n_F
; ++j
)
56 if (Vector_Equal(hull
->F
->p
[j
]+1, P
->Constraint
[i
]+1, P
->Dimension
))
60 hull
->set_lower_bound(hull
, P
->Constraint
[i
]+1, &lower
->p
[i
], options
);
61 value_oppose(upper
->p
[i
], P
->Constraint
[i
][1+P
->Dimension
]);
62 value_subtract(distances
->p
[i
], upper
->p
[i
], lower
->p
[i
]);
63 if (value_zero_p(distances
->p
[i
]))
65 if (i_min
== -1 || value_lt(distances
->p
[i
], distances
->p
[i_min
]))
71 value_decrement(*max
, upper
->p
[i_min
]);
72 value_assign(*min
, lower
->p
[i_min
]);
77 Vector_Free(distances
);
82 /* Return the (integer) vertices of P */
83 static Matrix
*Polyhedron_Vertices(Polyhedron
*P
)
86 Matrix
*M
= Matrix_Alloc(P
->NbRays
, P
->Dimension
+1);
88 for (i
= 0, j
= 0; i
< P
->NbRays
; ++i
) {
89 if (value_zero_p(P
->Ray
[i
][1+P
->Dimension
]))
91 assert(value_one_p(P
->Ray
[i
][1+P
->Dimension
]));
92 value_set_si(M
->p
[j
][P
->Dimension
], 1);
93 Vector_Copy(P
->Ray
[i
]+1, M
->p
[j
++], P
->Dimension
);
100 /* Set INCLUDE_KNOWN_FACETS_IN_LP to 1 to compute a lower bound for
101 * the minimal value wrt a facet based on the facets that have
102 * already been determined to be facets of the integer hull,
103 * rather than simply setting the lower bound to 0 or 1.
104 * This does not appear to be profitable, probably because
105 * the lower bound is recomputed every time we optimize
106 * wrt a facet. If we were to cache the lower bounds for
107 * facets that do not change when a new vertex is added,
108 * this may still turn out to be useful.
110 #define INCLUDE_KNOWN_FACETS_IN_LP 0
111 /* Set INCLUDE_KNOWN_FACETS_IN_ILP to 1 to take into account the
112 * facets that have already been determined to be facets
113 * of the integer hull while optimizing wrt some other
114 * facet. The idea is that the polytope over which to
115 * optimize could be flatter. However, in practice, adding
116 * these constraints seems to slow down the computation
117 * (on at least one example).
119 #define INCLUDE_KNOWN_FACETS_IN_ILP 0
121 Polyhedron
*add_facets(Polyhedron
*P
, Matrix
*facets
, int n
,
122 struct barvinok_options
*options
)
128 M
= Matrix_Alloc(P
->NbConstraints
+ n
, facets
->NbColumns
);
129 for (i
= 0; i
< n
; ++i
)
130 Vector_Copy(facets
->p
[i
], M
->p
[i
], facets
->NbColumns
);
131 for (i
= 0; i
< P
->NbConstraints
; ++i
)
132 Vector_Copy(P
->Constraint
[i
], M
->p
[n
+ i
], M
->NbColumns
);
133 R
= Constraints2Polyhedron(M
, options
->MaxRays
);
138 /* Add optimal and suboptimal points found by Polyhedron_Integer_Minimum
139 * to Q, replacing (and destroying) the old Q.
140 * rays is a temporary array for use in AddRays that can be reused
141 * over multiple calls and that is extended as needed.
143 Polyhedron
*add_points(Polyhedron
*Q
, Vector
*opt
, Matrix
*subopt
,
144 int n_subopt
, Matrix
*rays
,
145 struct barvinok_options
*options
)
150 if (1 + n_subopt
> rays
->NbRows
) {
151 int n
= rays
->NbRows
;
152 Matrix_Extend(rays
, 1+n_subopt
);
153 for (i
= n
; i
< rays
->NbRows
; ++i
) {
154 value_set_si(rays
->p
[i
][0], 1);
155 value_set_si(rays
->p
[i
][1+Q
->Dimension
], 1);
159 Vector_Copy(opt
->p
, rays
->p
[0]+1, Q
->Dimension
);
160 for (i
= 0; i
< n_subopt
; ++i
)
161 Vector_Copy(subopt
->p
[i
], rays
->p
[1+i
]+1, Q
->Dimension
);
163 R
= AddRays(rays
->p
[0], 1+n_subopt
, Q
, options
->MaxRays
);
169 /* Extends an initial approximation hull->init of the integer hull
170 * of hull->P using generalized basis reduction.
171 * In particular, it considers each facet of the current
172 * approximation add optimizes in the direction of the normal,
173 * adding the optimal point to the approximation as well as
174 * all other points that may have been found along the way,
175 * until no facet yields any more new points.
176 * Returns a list of the vertices of this integer hull.
178 static Matrix
*gbr_hull_extend(struct integer_hull
*hull
,
179 struct barvinok_options
*options
)
182 Polyhedron
*Q
= hull
->init
;
183 Matrix
*rays
= Matrix_Alloc(1, 2+Q
->Dimension
);
185 int n_candidates
= 0;
189 hull
->F
= Matrix_Alloc(Q
->NbConstraints
, 2+Q
->Dimension
);
192 candidates
= Matrix_Alloc(0, Q
->Dimension
);
197 value_set_si(rays
->p
[0][0], 1);
198 value_set_si(rays
->p
[0][1+Q
->Dimension
], 1);
200 while ((i_min
= select_best(hull
, Q
, &min
, &max
, options
)) != -1) {
205 if (INCLUDE_KNOWN_FACETS_IN_ILP
)
206 R
= add_facets(hull
->P
, hull
->F
, hull
->n_F
, options
);
211 opt
= Polyhedron_Integer_Minimum(R
, Q
->Constraint
[i_min
]+1,
212 min
, max
, candidates
, &n_candidates
,
214 candidates
->NbRows
= n_candidates
;
215 if (INCLUDE_KNOWN_FACETS_IN_ILP
)
218 hull
->n_F
= matrix_add(hull
->F
, hull
->n_F
, Q
->Constraint
[i_min
]);
223 Inner_Product(hull
->F
->p
[hull
->n_F
-1]+1, opt
->p
, Q
->Dimension
,
224 &hull
->F
->p
[hull
->n_F
-1][1+Q
->Dimension
]);
225 value_oppose(hull
->F
->p
[hull
->n_F
-1][1+Q
->Dimension
],
226 hull
->F
->p
[hull
->n_F
-1][1+Q
->Dimension
]);
228 Q
= add_points(Q
, opt
, candidates
, n_candidates
, rays
, options
);
233 vertices
= Polyhedron_Vertices(Q
);
242 Matrix_Free(hull
->F
);
244 Matrix_Free(candidates
);
249 /* Returns the Minkowski sum of the cone and the polytope
250 * that is the convex hull of its (integer) extremal rays.
252 static Polyhedron
*truncate_cone(Polyhedron
*C
,
253 struct barvinok_options
*options
)
259 V
= Matrix_Alloc(2*C
->NbRays
, 2+C
->Dimension
);
260 for (i
= 0; i
< C
->NbRays
; ++i
) {
261 if (value_notzero_p(C
->Ray
[i
][1+C
->Dimension
]))
263 Vector_Copy(C
->Ray
[i
], V
->p
[C
->NbRays
+i
], 1+C
->Dimension
+1);
264 Vector_Copy(C
->Ray
[i
], V
->p
[i
], 1+C
->Dimension
);
265 value_set_si(V
->p
[i
][1+C
->Dimension
], 1);
267 P
= Rays2Polyhedron(V
, options
->MaxRays
);
272 /* Frees original list and transformation matrix CV */
273 static Matrix
*transform_list_of_vertices(Matrix
*list
, Matrix
*CV
)
277 M
= Matrix_Alloc(list
->NbRows
, T
->NbColumns
);
278 Matrix_Product(list
, T
, M
);
286 /* Set the lower bound for optimizing along the normal of a facet
287 * in case of computing the integer hull of a truncated cone
288 * (i.e., a cone with the origin removed).
289 * In particular, if the constraint is one bounding the original
290 * cone, then the lower bound is zero; if it is a new constraint,
291 * then the lower bound is one.
292 * A more accurate bound can be obtained by looking at the
293 * already computed facets of the integer hull.
295 static void set_to_one(struct integer_hull
*hull
, Value
*obj
,
296 Value
*lower
, struct barvinok_options
*options
)
298 if (INCLUDE_KNOWN_FACETS_IN_LP
) {
303 if (hull
->F
->NbRows
< hull
->n_F
+hull
->P
->NbConstraints
)
304 Matrix_Extend(hull
->F
, hull
->n_F
+hull
->P
->NbConstraints
);
305 for (i
= 0; i
< hull
->P
->NbConstraints
; ++i
)
306 Vector_Copy(hull
->P
->Constraint
[i
], hull
->F
->p
[hull
->n_F
+i
],
310 value_set_si(one
, 1);
312 res
= constraints_opt(hull
->F
, obj
, one
, lp_min
, lower
, options
);
313 value_subtract(*lower
, *lower
, obj
[hull
->P
->Dimension
]);
314 assert(res
== lp_ok
);
316 if (value_notzero_p(*lower
))
320 if (value_zero_p(obj
[hull
->P
->Dimension
]))
321 value_set_si(*lower
, 0);
323 value_set_si(*lower
, 1);
326 /* Add to Q those points that the calling function already knows about,
327 * replacing (and destroying) the original Q.
329 static Polyhedron
*add_known_points(Polyhedron
*Q
,
330 Matrix
*c
, int n_c
, unsigned MaxRays
)
334 R
= AddRays(c
->p
[0], n_c
, Q
, MaxRays
);
339 /* Computes the integer hull of the (truncated) cone C
340 * using generalized basis reduction, where c contains a list
341 * of vertices that are known to be part of this integer hull.
343 static Matrix
*gbr_cone_hull(Polyhedron
*C
, Matrix
*c
, int n_c
,
344 struct barvinok_options
*options
)
348 struct integer_hull hull
;
350 POL_ENSURE_VERTICES(C
);
351 remove_all_equalities(&C
, NULL
, NULL
, &CV
, 0, options
->MaxRays
);
353 POL_ENSURE_VERTICES(C
);
356 hull
.init
= truncate_cone(C
, options
);
357 hull
.set_lower_bound
= set_to_one
;
359 hull
.init
= add_known_points(hull
.init
, c
, n_c
, options
->MaxRays
);
360 vertices
= gbr_hull_extend(&hull
, options
);
363 vertices
= transform_list_of_vertices(vertices
, CV
);
370 /* Computes the integer hull of the cone C with the origin removed. */
371 Matrix
*Cone_Integer_Hull(Polyhedron
*C
, Matrix
*candidates
,
372 int n_candidates
, struct barvinok_options
*options
)
374 switch(options
->integer_hull
) {
376 return gbr_cone_hull(C
, candidates
, n_candidates
, options
);
377 case BV_HULL_HILBERT
:
378 return Cone_Hilbert_Integer_Hull(C
, options
);
384 /* Computes initial full-dimensional approximation of the integer hull of P,
385 * or discovers an implicit equality of P.
386 * We start off with the integer vertices of P itself, if any.
387 * If there are no such vertices (available), then we simply
388 * take an arbitrary integer point in the polytope.
389 * Then we keep optimizing over normals of equalities in the description
390 * of the current approximation, until we find an equality that holds
391 * for the entire integer hull of P or until we have obtained a
392 * full-dimensional approximation.
394 static Polyhedron
*internal_polytope(Polyhedron
*P
,
395 struct barvinok_options
*options
)
399 Matrix
*vertices
= Matrix_Alloc(P
->NbRays
, P
->Dimension
+2);
401 for (i
= 0, j
= 0; i
< P
->NbRays
; ++i
) {
402 if (value_notone_p(P
->Ray
[i
][1+P
->Dimension
]))
404 Vector_Copy(P
->Ray
[i
], vertices
->p
[j
++], 2+P
->Dimension
);
406 vertices
->NbRows
= j
;
408 Vector
*sample
= Polyhedron_Sample(P
, options
);
410 value_set_si(vertices
->p
[0][1], 1);
411 Vector_Copy(sample
->p
, vertices
->p
[0]+1, P
->Dimension
+1);
413 vertices
->NbRows
= 1;
416 init
= Rays2Polyhedron(vertices
, options
->MaxRays
);
417 Matrix_Free(vertices
);
419 while (!emptyQ(init
) && init
->NbEq
) {
421 Vector
*v
= Vector_Alloc(init
->Dimension
+2);
424 value_set_si(v
->p
[0], 1);
425 Vector_Copy(init
->Constraint
[0]+1, v
->p
+1, init
->Dimension
+1);
426 value_decrement(v
->p
[1+init
->Dimension
],v
->p
[1+init
->Dimension
]);
428 Q
= AddConstraints(v
->p
, 1, P
, options
->MaxRays
);
429 sample
= Polyhedron_Sample(Q
, options
);
432 Vector_Oppose(init
->Constraint
[0]+1, v
->p
+1, init
->Dimension
+1);
433 value_decrement(v
->p
[1+init
->Dimension
],v
->p
[1+init
->Dimension
]);
435 Q
= AddConstraints(v
->p
, 1, P
, options
->MaxRays
);
436 sample
= Polyhedron_Sample(Q
, options
);
440 P
= AddConstraints(init
->Constraint
[0], 1, P
, options
->MaxRays
);
441 Polyhedron_Free(init
);
447 Vector_Copy(sample
->p
, v
->p
+1, init
->Dimension
+1);
448 Q
= AddRays(v
->p
, 1, init
, options
->MaxRays
);
449 Polyhedron_Free(init
);
459 /* Set the lower bound for optimizing along the normal of a facet
460 * in case of computing the integer hull of a polyhedron.
461 * Although obj contains the full affine objective function,
462 * the calling function expect a lower bound on the linear part only.
463 * We therefore need to subtract the constant from the computed
464 * optimum of the linear relaxation.
466 static void set_lower(struct integer_hull
*hull
, Value
*obj
,
467 Value
*lower
, struct barvinok_options
*options
)
473 value_set_si(one
, 1);
475 res
= polyhedron_opt(hull
->P
, obj
, one
, lp_min
, lower
, options
);
476 value_subtract(*lower
, *lower
, obj
[hull
->P
->Dimension
]);
477 assert(res
== lp_ok
);
482 /* Computes the vertices of the integer hull of P
484 Matrix
*Polyhedron_Integer_Hull(Polyhedron
*P
,
485 struct barvinok_options
*options
)
491 POL_ENSURE_VERTICES(P
);
492 remove_all_equalities(&P
, NULL
, NULL
, &CV
, 0, options
->MaxRays
);
494 POL_ENSURE_VERTICES(P
);
496 if (P
->Dimension
== 0) {
497 vertices
= Matrix_Alloc(1, 1);
498 value_set_si(vertices
->p
[0][0], 1);
500 init
= internal_polytope(P
, options
);
502 vertices
= Matrix_Alloc(0, P
->Dimension
+1);
503 Polyhedron_Free(init
);
504 } else if (init
->NbEq
) {
505 vertices
= Polyhedron_Integer_Hull(init
, options
);
506 Polyhedron_Free(init
);
508 struct integer_hull hull
;
511 hull
.set_lower_bound
= set_lower
;
512 vertices
= gbr_hull_extend(&hull
, options
);
517 vertices
= transform_list_of_vertices(vertices
, CV
);