3 #include <isl_set_polylib.h>
4 #include <barvinok/options.h>
5 #include <barvinok/util.h>
8 #include "lattice_width.h"
9 #include "param_util.h"
10 #include "reduce_domain.h"
12 #define ALLOC(type) (type*)malloc(sizeof(type))
13 #define ALLOCN(type,n) (type*)malloc((n) * sizeof(type))
15 static void clear_width_direction(struct width_direction
*wd
)
17 Vector_Free(wd
->width
);
20 Polyhedron_Free(wd
->domain
);
23 static struct width_direction_array
*new_width_direction_array(void)
25 struct width_direction_array
*dirs
= ALLOC(struct width_direction_array
);
29 dirs
->wd
= ALLOCN(struct width_direction
, dirs
->alloc
);
34 static void grow_width_direction_array(struct width_direction_array
*dirs
,
37 if (dirs
->n
+ extra
<= dirs
->alloc
)
39 dirs
->alloc
= (5*(dirs
->n
+extra
))/4;
40 dirs
->wd
= (struct width_direction
*)realloc(dirs
->wd
,
41 dirs
->alloc
* sizeof(struct width_direction
));
45 void free_width_direction_array(struct width_direction_array
*dirs
)
49 for (i
= 0; i
< dirs
->n
; ++i
)
50 clear_width_direction(&dirs
->wd
[i
]);
55 #define INT_BITS (sizeof(unsigned) * 8)
57 /* For each parametric vertex, compute cone of directions
58 * for which this vertex attains the minimal value.
60 static Matrix
**compute_vertex_dirs(Param_Polyhedron
*PP
)
63 unsigned nvar
= PP
->V
->Vertex
->NbRows
;
65 Matrix
**vertex_dirs
= ALLOCN(Matrix
*, PP
->nbV
);
67 for (i
= 0, V
= PP
->V
; V
; ++i
, V
= V
->next
) {
77 int len
= (PP
->Constraints
->NbRows
+INT_BITS
-1)/INT_BITS
;
79 n
= bit_vector_count(facets
, len
);
81 facets
= supporting_constraints(PP
->Constraints
, V
, &n
);
82 M
= Matrix_Alloc(n
, 1+nvar
+1);
83 for (k
= 0, j
= 0, kx
= 0, bx
= MSB
; j
< n
; ++k
) {
84 if (facets
[kx
] & bx
) {
85 value_set_si(M
->p
[j
][0], 1);
86 Vector_Copy(PP
->Constraints
->p
[k
]+1, M
->p
[j
++]+1, nvar
);
90 P
= Constraints2Polyhedron(M
, 0);
92 vertex_dirs
[i
] = Matrix_Alloc(P
->NbRays
-1, nvar
);
93 for (k
= 0, j
= 0; k
< P
->NbRays
; ++k
) {
94 if (value_notzero_p(P
->Ray
[k
][1+nvar
]))
96 Vector_Copy(P
->Ray
[k
]+1, vertex_dirs
[i
]->p
[j
++], nvar
);
111 static void Vector_Subtract(Value
*a
, Value a_d
,
113 Value
*c
, Value
*c_d
, int len
)
118 value_lcm(*c_d
, a_d
, b_d
);
119 value_divexact(ma
, *c_d
, a_d
);
120 value_divexact(mb
, *c_d
, b_d
);
121 value_oppose(mb
, mb
);
122 Vector_Combine(a
, b
, c
, ma
, mb
, len
);
127 /* Compute width for a given direction dir and initialize width_direction
130 static void compute_width_direction(Matrix
*V_min
, Matrix
*V_max
,
131 Value
*dir
, struct width_direction
*wd
)
133 Vector
*max
= Vector_Alloc(V_min
->NbColumns
);
134 unsigned nvar
= V_min
->NbRows
;
135 unsigned nparam
= V_min
->NbColumns
-2;
137 wd
->width
= Vector_Alloc(V_min
->NbColumns
);
138 wd
->dir
= Vector_Alloc(nvar
);
139 Vector_Copy(dir
, wd
->dir
->p
, nvar
);
145 Vector_Matrix_Product(dir
, V_max
, max
->p
);
146 Vector_Matrix_Product(dir
, V_min
, wd
->width
->p
);
147 Vector_Subtract(max
->p
, V_max
->p
[0][V_max
->NbColumns
],
148 wd
->width
->p
, V_min
->p
[0][V_min
->NbColumns
],
149 wd
->width
->p
, &wd
->width
->p
[nparam
+1],
155 Vector_Normalize(wd
->width
->p
, nparam
+2);
160 static int Vector_Compare(Value
*p1
, Value
*p2
, unsigned len
)
164 for (i
= 0; i
< len
; ++i
) {
165 int sign
= mpz_cmp(p1
[i
], p2
[i
]);
172 static int wd_width_lex_cmp(const void *va
, const void *vb
)
174 const struct width_direction
*a
= (const struct width_direction
*)va
;
175 const struct width_direction
*b
= (const struct width_direction
*)vb
;
177 return Vector_Compare(a
->width
->p
, b
->width
->p
, a
->width
->Size
);
180 static int wd_dir_lex_cmp(const void *va
, const void *vb
)
182 const struct width_direction
*a
= (const struct width_direction
*)va
;
183 const struct width_direction
*b
= (const struct width_direction
*)vb
;
185 return Vector_Compare(a
->dir
->p
, b
->dir
->p
, a
->dir
->Size
);
188 static int add_vertex(Matrix
*M
, int n
, Value
*v
)
191 Matrix_Extend(M
, 3*(M
->NbRows
+10)/2);
192 value_set_si(M
->p
[n
][0], 1);
193 Vector_Copy(v
, M
->p
[n
]+1, M
->NbColumns
-2);
194 value_set_si(M
->p
[n
][M
->NbColumns
-1], 1);
198 /* Puts the points in v that lie in P in front of the list
199 * and returns their number.
201 static int valid_vertices(Polyhedron
*P
, Matrix
*v
, int n_v
)
206 assert(v
->NbColumns
== P
->Dimension
+2);
209 for (j
= 0, k
= 0; j
< n_v
; ++j
) {
210 for (i
= 0; i
< P
->NbConstraints
; ++i
) {
211 Inner_Product(v
->p
[j
]+1, P
->Constraint
[i
]+1, P
->Dimension
+1, &tmp
);
212 if (value_neg_p(tmp
))
215 if (i
< P
->NbConstraints
)
218 Vector_Exchange(v
->p
[j
]+1, v
->p
[k
]+1, P
->Dimension
);
226 static struct width_direction_array
*
227 compute_width_directions(Param_Polyhedron
*PP
, struct barvinok_options
*options
)
229 Matrix
**vertex_dirs
;
230 Param_Vertices
*V_max
, *V_min
;
231 int i
, V_max_i
, V_min_i
;
232 unsigned nvar
= PP
->V
->Vertex
->NbRows
;
233 struct width_direction_array
*width_dirs
= new_width_direction_array();
234 Matrix
*all_vertices
= Matrix_Alloc(nvar
, 1+nvar
+1);
237 vertex_dirs
= compute_vertex_dirs(PP
);
239 for (V_max
= PP
->V
; V_max
; V_max
= V_max
->next
)
240 Param_Vertex_Common_Denominator(V_max
);
242 for (V_max
= PP
->V
, V_max_i
= 0; V_max
; V_max
= V_max
->next
, V_max_i
++) {
243 for (V_min
= V_max
->next
, V_min_i
= V_max_i
+1;
245 V_min
= V_min
->next
, V_min_i
++) {
249 unsigned V_max_n
= vertex_dirs
[V_max_i
]->NbRows
;
250 unsigned V_min_n
= vertex_dirs
[V_min_i
]->NbRows
;
254 if (options
->verbose
)
255 fprintf(stderr
, "%d/%d %d/%d %d \r",
260 M
= Matrix_Alloc(V_max_n
+V_min_n
, 1+nvar
+1);
261 for (i
= 0; i
< V_max_n
; ++i
) {
262 value_set_si(M
->p
[i
][0], 1);
263 Vector_Oppose(vertex_dirs
[V_max_i
]->p
[i
], M
->p
[i
]+1, nvar
);
265 for (i
= 0; i
< V_min_n
; ++i
) {
266 value_set_si(M
->p
[V_max_n
+i
][0], 1);
267 Vector_Copy(vertex_dirs
[V_min_i
]->p
[i
], M
->p
[V_max_n
+i
]+1, nvar
);
269 C
= Constraints2Polyhedron(M
, options
->MaxRays
);
271 n_valid
= valid_vertices(C
, all_vertices
, n_vertices
);
272 basis
= Cone_Integer_Hull(C
, all_vertices
, n_valid
, options
);
273 grow_width_direction_array(width_dirs
, basis
->NbRows
);
274 qsort(width_dirs
->wd
, width_dirs
->n
, sizeof(struct width_direction
),
276 sorted_n
= width_dirs
->n
;
277 for (i
= 0; i
< basis
->NbRows
; ++i
) {
279 struct width_direction wd
;
280 Matrix
*VM_min
, *VM_max
;
283 VM_min
= V_min
->Vertex
;
284 VM_max
= V_max
->Vertex
;
285 pos
= First_Non_Zero(basis
->p
[i
], nvar
);
286 if (value_neg_p(basis
->p
[i
][pos
])) {
287 Vector_Oppose(basis
->p
[i
], basis
->p
[i
], nvar
);
288 VM_min
= V_max
->Vertex
;
289 VM_max
= V_min
->Vertex
;
295 if (bsearch(&wd
, width_dirs
->wd
, sorted_n
,
296 sizeof(struct width_direction
),
300 n_vertices
= add_vertex(all_vertices
, n_vertices
, basis
->p
[i
]);
301 compute_width_direction(VM_min
, VM_max
, basis
->p
[i
],
302 &width_dirs
->wd
[width_dirs
->n
++]);
308 Matrix_Free(all_vertices
);
310 for (i
= 0; i
< PP
->nbV
; ++i
)
311 Matrix_Free(vertex_dirs
[i
]);
317 /* Computes the lattice width direction of a parametric polytope.
318 * The parameter space is allowed to be unbounded.
319 * Currently, the parametric polytope and the parameter space
320 * are assumed to be full-dimensional.
322 * First, we compute the parametric vertices.
323 * Then, for each pair of vertices, we construct a (rational) cone
324 * of directions for which one vertex attains the minimal value
325 * and the other vertex attians the maximal value.
326 * The candidate directions are the elements of the integer hulls
328 * The minimal direction is then obtained by computing the
329 * region in the parameter space where each direction yields
330 * a smaller (or equal) width than all the other directions.
332 * In principle, we can avoid computing candidate directions
333 * for vertices with no overlapping activity domains (possibly
334 * after opening some facets of the activity domains in the
337 * The output is a list of triples, consisting of a direction,
338 * the corresponding width and the chamber in the parameter
339 * space where this direction leads to the minimal width.
341 * The algorithm is described in "Integer points in a parameterised
342 * polyhedron" by Friedrich Eisenbrand and Gennady Shmonin.
344 struct width_direction_array
*
345 Polyhedron_Lattice_Width_Directions(Polyhedron
*P
, Polyhedron
*C
,
346 struct barvinok_options
*options
)
348 Param_Polyhedron
*PP
;
349 unsigned nparam
= C
->Dimension
;
351 struct width_direction_array
*width_dirs
;
355 assert(P
->NbEq
== 0);
356 assert(C
->NbEq
== 0);
358 /* Use true context since the algorithm assumes P is non-empty
359 * for every point in the context.
361 TC
= true_context(P
, C
, options
->MaxRays
);
362 inner
= inner_point(TC
);
364 /* This is overkill, as we discard the computed chambers. */
365 PP
= Polyhedron2Param_Polyhedron(P
, TC
, options
);
367 width_dirs
= compute_width_directions(PP
, options
);
368 Param_Polyhedron_Free(PP
);
370 qsort(width_dirs
->wd
, width_dirs
->n
, sizeof(struct width_direction
),
373 for (i
= 1, j
= 1; i
< width_dirs
->n
; ++i
) {
374 /* We could also weed out width_directions that differ by a
375 * (positive) constant from another width_direction, but then
376 * we'd have to put the two width_directions on a common
379 if (Vector_Equal(width_dirs
->wd
[j
-1].width
->p
,
380 width_dirs
->wd
[i
].width
->p
, nparam
+2))
381 clear_width_direction(&width_dirs
->wd
[i
]);
383 width_dirs
->wd
[j
++] = width_dirs
->wd
[i
];
387 for (i
= 0, k
= 0; i
< width_dirs
->n
; ++i
) {
388 Matrix
*M
= Matrix_Alloc(TC
->NbConstraints
+width_dirs
->n
-(i
-k
)-1, nparam
+2);
389 for (j
= 0; j
< TC
->NbConstraints
; ++j
)
390 Vector_Copy(TC
->Constraint
[j
], M
->p
[j
], nparam
+2);
391 for (j
= 0; j
< width_dirs
->n
; ++j
) {
393 if (k
<= j
&& j
<= i
)
396 pos
= TC
->NbConstraints
+ j
;
398 pos
= TC
->NbConstraints
+ j
- (i
-k
) - 1;
399 Vector_Subtract(width_dirs
->wd
[j
].width
->p
,
400 width_dirs
->wd
[j
].width
->p
[nparam
+1],
401 width_dirs
->wd
[i
].width
->p
,
402 width_dirs
->wd
[i
].width
->p
[nparam
+1],
403 M
->p
[pos
]+1, M
->p
[pos
], nparam
+1);
404 value_set_si(M
->p
[pos
][0], 1);
405 Vector_Normalize(M
->p
[pos
]+1, nparam
+1);
406 if (!is_internal(inner
, M
->p
[pos
]))
407 value_decrement(M
->p
[pos
][nparam
+1], M
->p
[pos
][nparam
+1]);
409 width_dirs
->wd
[i
].domain
= Constraints2Polyhedron(M
, options
->MaxRays
);
410 if (emptyQ(width_dirs
->wd
[i
].domain
))
411 clear_width_direction(&width_dirs
->wd
[i
]);
413 width_dirs
->wd
[k
++] = width_dirs
->wd
[i
];
423 /* Construct evalue of chambers with their associated widths */
424 evalue
*Polyhedron_Lattice_Width(Polyhedron
*P
, Polyhedron
*C
,
425 struct barvinok_options
*options
)
428 struct evalue_section
*s
;
429 struct width_direction_array
*width_dirs
;
431 unsigned nparam
= C
->Dimension
;
433 width_dirs
= Polyhedron_Lattice_Width_Directions(P
, C
, options
);
434 s
= ALLOCN(struct evalue_section
, width_dirs
->n
);
435 for (i
= 0; i
< width_dirs
->n
; ++i
) {
436 s
[i
].D
= width_dirs
->wd
[i
].domain
;
437 width_dirs
->wd
[i
].domain
= NULL
;
438 s
[i
].E
= affine2evalue(width_dirs
->wd
[i
].width
->p
,
439 width_dirs
->wd
[i
].width
->p
[nparam
+1],
442 free_width_direction_array(width_dirs
);
444 width
= evalue_from_section_array(s
, i
);
450 __isl_give isl_pw_qpolynomial
*isl_basic_set_lattice_width(
451 __isl_take isl_basic_set
*bset
)
455 isl_pw_qpolynomial
*pwqp
;
460 struct barvinok_options
*options
;
461 int options_allocated
= 0;
466 ctx
= isl_basic_set_get_ctx(bset
);
467 options
= isl_ctx_peek_barvinok_options(ctx
);
469 options
= barvinok_options_new_with_defaults();
470 options_allocated
= 1;
473 nparam
= isl_basic_set_dim(bset
, isl_dim_param
);
474 dim
= isl_basic_set_get_dim(bset
);
475 dim
= isl_dim_domain(isl_dim_from_range(dim
));
477 U
= Universe_Polyhedron(nparam
);
478 P
= isl_basic_set_to_polylib(bset
);
480 E
= Polyhedron_Lattice_Width(P
, U
, options
);
482 pwqp
= isl_pw_qpolynomial_from_evalue(dim
, E
);
483 isl_basic_set_free(bset
);
488 if (options_allocated
)
489 barvinok_options_free(options
);
494 __isl_give isl_pw_qpolynomial
*isl_set_lattice_width(__isl_take isl_set
*set
)
499 if (isl_set_fast_is_empty(set
)) {
501 dim
= isl_set_get_dim(set
);
502 dim
= isl_dim_domain(isl_dim_from_range(dim
));
504 return isl_pw_qpolynomial_zero(dim
);
507 if (isl_set_n_basic_set(set
) != 1)
508 isl_die(isl_set_get_ctx(set
), isl_error_unsupported
,
509 "unions not supported (yet)", goto error
);
511 return isl_basic_set_lattice_width(isl_set_simple_hull(set
));
517 static int set_lw(__isl_take isl_set
*set
, void *user
)
519 isl_union_pw_qpolynomial
**res
= (isl_union_pw_qpolynomial
**)user
;
520 isl_pw_qpolynomial
*pwqp
;
522 pwqp
= isl_set_lattice_width(set
);
523 *res
= isl_union_pw_qpolynomial_add_pw_qpolynomial(*res
, pwqp
);
528 __isl_give isl_union_pw_qpolynomial
*isl_union_set_lattice_width(
529 __isl_take isl_union_set
*uset
)
532 isl_union_pw_qpolynomial
*res
;
534 dim
= isl_union_set_get_dim(uset
);
535 res
= isl_union_pw_qpolynomial_zero(dim
);
536 if (isl_union_set_foreach_set(uset
, &set_lw
, &res
) < 0)
538 isl_union_set_free(uset
);
542 isl_union_set_free(uset
);
543 isl_union_pw_qpolynomial_free(res
);