1 | /* |
2 | * Copyright 2008-2009 Katholieke Universiteit Leuven |
3 | * |
4 | * Use of this software is governed by the MIT license |
5 | * |
6 | * Written by Sven Verdoolaege, K.U.Leuven, Departement |
7 | * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium |
8 | */ |
9 | |
10 | #include <isl_ctx_private.h> |
11 | #include <isl_map_private.h> |
12 | #include <isl/lp.h> |
13 | #include <isl_seq.h> |
14 | #include "isl_tab.h" |
15 | #include <isl_options_private.h> |
16 | #include <isl_local_space_private.h> |
17 | #include <isl_aff_private.h> |
18 | #include <isl_mat_private.h> |
19 | #include <isl_val_private.h> |
20 | #include <isl_vec_private.h> |
21 | |
22 | #include <bset_to_bmap.c> |
23 | #include <set_to_map.c> |
24 | |
25 | static enum isl_lp_result isl_tab_solve_lp(__isl_keep isl_basic_map *bmap, |
26 | int maximize, isl_int *f, isl_int denom, isl_int *opt, |
27 | isl_int *opt_denom, __isl_give isl_vec **sol) |
28 | { |
29 | struct isl_tab *tab; |
30 | enum isl_lp_result res; |
31 | isl_size dim = isl_basic_map_dim(bmap, type: isl_dim_all); |
32 | |
33 | if (dim < 0) |
34 | return isl_lp_error; |
35 | if (maximize) |
36 | isl_seq_neg(dst: f, src: f, len: 1 + dim); |
37 | |
38 | bmap = isl_basic_map_gauss(bmap, NULL); |
39 | tab = isl_tab_from_basic_map(bmap, track: 0); |
40 | res = isl_tab_min(tab, f, denom, opt, opt_denom, flags: 0); |
41 | if (res == isl_lp_ok && sol) { |
42 | *sol = isl_tab_get_sample_value(tab); |
43 | if (!*sol) |
44 | res = isl_lp_error; |
45 | } |
46 | isl_tab_free(tab); |
47 | |
48 | if (maximize) |
49 | isl_seq_neg(dst: f, src: f, len: 1 + dim); |
50 | if (maximize && opt) |
51 | isl_int_neg(*opt, *opt); |
52 | |
53 | return res; |
54 | } |
55 | |
56 | /* Given a basic map "bmap" and an affine combination of the variables "f" |
57 | * with denominator "denom", set *opt / *opt_denom to the minimal |
58 | * (or maximal if "maximize" is true) value attained by f/d over "bmap", |
59 | * assuming the basic map is not empty and the expression cannot attain |
60 | * arbitrarily small (or large) values. |
61 | * If opt_denom is NULL, then *opt is rounded up (or down) |
62 | * to the nearest integer. |
63 | * The return value reflects the nature of the result (empty, unbounded, |
64 | * minimal or maximal value returned in *opt). |
65 | */ |
66 | enum isl_lp_result isl_basic_map_solve_lp(__isl_keep isl_basic_map *bmap, |
67 | int max, isl_int *f, isl_int d, isl_int *opt, isl_int *opt_denom, |
68 | __isl_give isl_vec **sol) |
69 | { |
70 | if (sol) |
71 | *sol = NULL; |
72 | |
73 | if (!bmap) |
74 | return isl_lp_error; |
75 | |
76 | return isl_tab_solve_lp(bmap, maximize: max, f, denom: d, opt, opt_denom, sol); |
77 | } |
78 | |
79 | enum isl_lp_result isl_basic_set_solve_lp(__isl_keep isl_basic_set *bset, |
80 | int max, isl_int *f, isl_int d, isl_int *opt, isl_int *opt_denom, |
81 | __isl_give isl_vec **sol) |
82 | { |
83 | return isl_basic_map_solve_lp(bmap: bset_to_bmap(bset), max, |
84 | f, d, opt, opt_denom, sol); |
85 | } |
86 | |
87 | enum isl_lp_result isl_map_solve_lp(__isl_keep isl_map *map, int max, |
88 | isl_int *f, isl_int d, isl_int *opt, |
89 | isl_int *opt_denom, |
90 | __isl_give isl_vec **sol) |
91 | { |
92 | int i; |
93 | isl_int o; |
94 | isl_int t; |
95 | isl_int opt_i; |
96 | isl_int opt_denom_i; |
97 | enum isl_lp_result res; |
98 | int max_div; |
99 | isl_vec *v = NULL; |
100 | |
101 | if (!map) |
102 | return isl_lp_error; |
103 | if (map->n == 0) |
104 | return isl_lp_empty; |
105 | |
106 | max_div = 0; |
107 | for (i = 0; i < map->n; ++i) |
108 | if (map->p[i]->n_div > max_div) |
109 | max_div = map->p[i]->n_div; |
110 | if (max_div > 0) { |
111 | isl_size total = isl_map_dim(map, type: isl_dim_all); |
112 | if (total < 0) |
113 | return isl_lp_error; |
114 | v = isl_vec_alloc(ctx: map->ctx, size: 1 + total + max_div); |
115 | if (!v) |
116 | return isl_lp_error; |
117 | isl_seq_cpy(dst: v->el, src: f, len: 1 + total); |
118 | isl_seq_clr(p: v->el + 1 + total, len: max_div); |
119 | f = v->el; |
120 | } |
121 | |
122 | if (!opt && map->n > 1 && sol) { |
123 | isl_int_init(o); |
124 | opt = &o; |
125 | } |
126 | if (map->n > 0) |
127 | isl_int_init(opt_i); |
128 | if (map->n > 0 && opt_denom) { |
129 | isl_int_init(opt_denom_i); |
130 | isl_int_init(t); |
131 | } |
132 | |
133 | res = isl_basic_map_solve_lp(bmap: map->p[0], max, f, d, |
134 | opt, opt_denom, sol); |
135 | if (res == isl_lp_error || res == isl_lp_unbounded) |
136 | goto done; |
137 | |
138 | if (sol) |
139 | *sol = NULL; |
140 | |
141 | for (i = 1; i < map->n; ++i) { |
142 | isl_vec *sol_i = NULL; |
143 | enum isl_lp_result res_i; |
144 | int better; |
145 | |
146 | res_i = isl_basic_map_solve_lp(bmap: map->p[i], max, f, d, |
147 | opt: &opt_i, |
148 | opt_denom: opt_denom ? &opt_denom_i : NULL, |
149 | sol: sol ? &sol_i : NULL); |
150 | if (res_i == isl_lp_error || res_i == isl_lp_unbounded) { |
151 | res = res_i; |
152 | goto done; |
153 | } |
154 | if (res_i == isl_lp_empty) |
155 | continue; |
156 | if (res == isl_lp_empty) { |
157 | better = 1; |
158 | } else if (!opt_denom) { |
159 | if (max) |
160 | better = isl_int_gt(opt_i, *opt); |
161 | else |
162 | better = isl_int_lt(opt_i, *opt); |
163 | } else { |
164 | isl_int_mul(t, opt_i, *opt_denom); |
165 | isl_int_submul(t, *opt, opt_denom_i); |
166 | if (max) |
167 | better = isl_int_is_pos(t); |
168 | else |
169 | better = isl_int_is_neg(t); |
170 | } |
171 | if (better) { |
172 | res = res_i; |
173 | if (opt) |
174 | isl_int_set(*opt, opt_i); |
175 | if (opt_denom) |
176 | isl_int_set(*opt_denom, opt_denom_i); |
177 | if (sol) { |
178 | isl_vec_free(vec: *sol); |
179 | *sol = sol_i; |
180 | } |
181 | } else |
182 | isl_vec_free(vec: sol_i); |
183 | } |
184 | |
185 | done: |
186 | isl_vec_free(vec: v); |
187 | if (map->n > 0 && opt_denom) { |
188 | isl_int_clear(opt_denom_i); |
189 | isl_int_clear(t); |
190 | } |
191 | if (map->n > 0) |
192 | isl_int_clear(opt_i); |
193 | if (opt == &o) |
194 | isl_int_clear(o); |
195 | return res; |
196 | } |
197 | |
198 | enum isl_lp_result isl_set_solve_lp(__isl_keep isl_set *set, int max, |
199 | isl_int *f, isl_int d, isl_int *opt, |
200 | isl_int *opt_denom, |
201 | __isl_give isl_vec **sol) |
202 | { |
203 | return isl_map_solve_lp(map: set_to_map(set), max, |
204 | f, d, opt, opt_denom, sol); |
205 | } |
206 | |
207 | /* Return the optimal (rational) value of "obj" over "bset", assuming |
208 | * that "obj" and "bset" have aligned parameters and divs. |
209 | * If "max" is set, then the maximal value is computed. |
210 | * Otherwise, the minimal value is computed. |
211 | * |
212 | * Return infinity or negative infinity if the optimal value is unbounded and |
213 | * NaN if "bset" is empty. |
214 | * |
215 | * Call isl_basic_set_solve_lp and translate the results. |
216 | */ |
217 | static __isl_give isl_val *basic_set_opt_lp( |
218 | __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj) |
219 | { |
220 | isl_ctx *ctx; |
221 | isl_val *res; |
222 | enum isl_lp_result lp_res; |
223 | |
224 | if (!bset || !obj) |
225 | return NULL; |
226 | |
227 | ctx = isl_aff_get_ctx(aff: obj); |
228 | res = isl_val_alloc(ctx); |
229 | if (!res) |
230 | return NULL; |
231 | lp_res = isl_basic_set_solve_lp(bset, max, f: obj->v->el + 1, |
232 | d: obj->v->el[0], opt: &res->n, opt_denom: &res->d, NULL); |
233 | if (lp_res == isl_lp_ok) |
234 | return isl_val_normalize(v: res); |
235 | isl_val_free(v: res); |
236 | if (lp_res == isl_lp_error) |
237 | return NULL; |
238 | if (lp_res == isl_lp_empty) |
239 | return isl_val_nan(ctx); |
240 | if (max) |
241 | return isl_val_infty(ctx); |
242 | else |
243 | return isl_val_neginfty(ctx); |
244 | } |
245 | |
246 | /* Return the optimal (rational) value of "obj" over "bset", assuming |
247 | * that "obj" and "bset" have aligned parameters. |
248 | * If "max" is set, then the maximal value is computed. |
249 | * Otherwise, the minimal value is computed. |
250 | * |
251 | * Return infinity or negative infinity if the optimal value is unbounded and |
252 | * NaN if "bset" is empty. |
253 | * |
254 | * Align the divs of "bset" and "obj" and call basic_set_opt_lp. |
255 | */ |
256 | static __isl_give isl_val *isl_basic_set_opt_lp_val_aligned( |
257 | __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj) |
258 | { |
259 | int *exp1 = NULL; |
260 | int *exp2 = NULL; |
261 | isl_ctx *ctx; |
262 | isl_mat *bset_div = NULL; |
263 | isl_mat *div = NULL; |
264 | isl_val *res; |
265 | isl_size bset_n_div, obj_n_div; |
266 | |
267 | if (!bset || !obj) |
268 | return NULL; |
269 | |
270 | ctx = isl_aff_get_ctx(aff: obj); |
271 | if (!isl_space_is_equal(space1: bset->dim, space2: obj->ls->dim)) |
272 | isl_die(ctx, isl_error_invalid, |
273 | "spaces don't match" , return NULL); |
274 | |
275 | bset_n_div = isl_basic_set_dim(bset, type: isl_dim_div); |
276 | obj_n_div = isl_aff_dim(aff: obj, type: isl_dim_div); |
277 | if (bset_n_div < 0 || obj_n_div < 0) |
278 | return NULL; |
279 | if (bset_n_div == 0 && obj_n_div == 0) |
280 | return basic_set_opt_lp(bset, max, obj); |
281 | |
282 | bset = isl_basic_set_copy(bset); |
283 | obj = isl_aff_copy(aff: obj); |
284 | |
285 | bset_div = isl_basic_set_get_divs(bset); |
286 | exp1 = isl_alloc_array(ctx, int, bset_n_div); |
287 | exp2 = isl_alloc_array(ctx, int, obj_n_div); |
288 | if (!bset_div || (bset_n_div && !exp1) || (obj_n_div && !exp2)) |
289 | goto error; |
290 | |
291 | div = isl_merge_divs(div1: bset_div, div2: obj->ls->div, exp1, exp2); |
292 | |
293 | bset = isl_basic_set_expand_divs(bset, div: isl_mat_copy(mat: div), exp: exp1); |
294 | obj = isl_aff_expand_divs(aff: obj, div: isl_mat_copy(mat: div), exp: exp2); |
295 | |
296 | res = basic_set_opt_lp(bset, max, obj); |
297 | |
298 | isl_mat_free(mat: bset_div); |
299 | isl_mat_free(mat: div); |
300 | free(ptr: exp1); |
301 | free(ptr: exp2); |
302 | isl_basic_set_free(bset); |
303 | isl_aff_free(aff: obj); |
304 | |
305 | return res; |
306 | error: |
307 | isl_mat_free(mat: div); |
308 | isl_mat_free(mat: bset_div); |
309 | free(ptr: exp1); |
310 | free(ptr: exp2); |
311 | isl_basic_set_free(bset); |
312 | isl_aff_free(aff: obj); |
313 | return NULL; |
314 | } |
315 | |
316 | /* Return the optimal (rational) value of "obj" over "bset". |
317 | * If "max" is set, then the maximal value is computed. |
318 | * Otherwise, the minimal value is computed. |
319 | * |
320 | * Return infinity or negative infinity if the optimal value is unbounded and |
321 | * NaN if "bset" is empty. |
322 | */ |
323 | static __isl_give isl_val *isl_basic_set_opt_lp_val( |
324 | __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj) |
325 | { |
326 | isl_bool equal; |
327 | isl_val *res; |
328 | |
329 | if (!bset || !obj) |
330 | return NULL; |
331 | |
332 | equal = isl_basic_set_space_has_equal_params(bset, space: obj->ls->dim); |
333 | if (equal < 0) |
334 | return NULL; |
335 | if (equal) |
336 | return isl_basic_set_opt_lp_val_aligned(bset, max, obj); |
337 | |
338 | bset = isl_basic_set_copy(bset); |
339 | obj = isl_aff_copy(aff: obj); |
340 | bset = isl_basic_set_align_params(bset, model: isl_aff_get_domain_space(aff: obj)); |
341 | obj = isl_aff_align_params(aff: obj, model: isl_basic_set_get_space(bset)); |
342 | |
343 | res = isl_basic_set_opt_lp_val_aligned(bset, max, obj); |
344 | |
345 | isl_basic_set_free(bset); |
346 | isl_aff_free(aff: obj); |
347 | |
348 | return res; |
349 | } |
350 | |
351 | /* Return the minimal (rational) value of "obj" over "bset". |
352 | * |
353 | * Return negative infinity if the minimal value is unbounded and |
354 | * NaN if "bset" is empty. |
355 | */ |
356 | __isl_give isl_val *isl_basic_set_min_lp_val(__isl_keep isl_basic_set *bset, |
357 | __isl_keep isl_aff *obj) |
358 | { |
359 | return isl_basic_set_opt_lp_val(bset, max: 0, obj); |
360 | } |
361 | |
362 | /* Return the maximal (rational) value of "obj" over "bset". |
363 | * |
364 | * Return infinity if the maximal value is unbounded and |
365 | * NaN if "bset" is empty. |
366 | */ |
367 | __isl_give isl_val *isl_basic_set_max_lp_val(__isl_keep isl_basic_set *bset, |
368 | __isl_keep isl_aff *obj) |
369 | { |
370 | return isl_basic_set_opt_lp_val(bset, max: 1, obj); |
371 | } |
372 | |