isl_basic_set_solve_ilp: fix handling of sets with equalities
[platform/upstream/isl.git] / isl_ilp.c
1 #include "isl_ilp.h"
2 #include "isl_map_private.h"
3 #include "isl_sample.h"
4 #include "isl_seq.h"
5 #include "isl_equalities.h"
6
7 /* Given a basic set "bset", construct a basic set U such that for
8  * each element x in U, the whole unit box positioned at x is inside
9  * the given basic set.
10  * Note that U may not contain all points that satisfy this property.
11  *
12  * We simply add the sum of all negative coefficients to the constant
13  * term.  This ensures that if x satisfies the resulting constraints,
14  * then x plus any sum of unit vectors satisfies the original constraints.
15  */
16 static struct isl_basic_set *unit_box_base_points(struct isl_basic_set *bset)
17 {
18         int i, j, k;
19         struct isl_basic_set *unit_box = NULL;
20         unsigned total;
21
22         if (!bset)
23                 goto error;
24
25         if (bset->n_eq != 0) {
26                 unit_box = isl_basic_set_empty_like(bset);
27                 isl_basic_set_free(bset);
28                 return unit_box;
29         }
30
31         total = isl_basic_set_total_dim(bset);
32         unit_box = isl_basic_set_alloc_dim(isl_basic_set_get_dim(bset),
33                                         0, 0, bset->n_ineq);
34
35         for (i = 0; i < bset->n_ineq; ++i) {
36                 k = isl_basic_set_alloc_inequality(unit_box);
37                 if (k < 0)
38                         goto error;
39                 isl_seq_cpy(unit_box->ineq[k], bset->ineq[i], 1 + total);
40                 for (j = 0; j < total; ++j) {
41                         if (isl_int_is_nonneg(unit_box->ineq[k][1 + j]))
42                                 continue;
43                         isl_int_add(unit_box->ineq[k][0],
44                                 unit_box->ineq[k][0], unit_box->ineq[k][1 + j]);
45                 }
46         }
47
48         isl_basic_set_free(bset);
49         return unit_box;
50 error:
51         isl_basic_set_free(bset);
52         isl_basic_set_free(unit_box);
53         return NULL;
54 }
55
56 /* Find an integer point in "bset", preferably one that is
57  * close to minimizing "f".
58  *
59  * We first check if we can easily put unit boxes inside bset.
60  * If so, we take the best base point of any of the unit boxes we can find
61  * and round it up to the nearest integer.
62  * If not, we simply pick any integer point in "bset".
63  */
64 static struct isl_vec *initial_solution(struct isl_basic_set *bset, isl_int *f)
65 {
66         enum isl_lp_result res;
67         struct isl_basic_set *unit_box;
68         struct isl_vec *sol;
69
70         unit_box = unit_box_base_points(isl_basic_set_copy(bset));
71
72         res = isl_basic_set_solve_lp(unit_box, 0, f, bset->ctx->one,
73                                         NULL, NULL, &sol);
74         if (res == isl_lp_ok) {
75                 isl_basic_set_free(unit_box);
76                 return isl_vec_ceil(sol);
77         }
78
79         isl_basic_set_free(unit_box);
80
81         return isl_basic_set_sample_vec(isl_basic_set_copy(bset));
82 }
83
84 /* Restrict "bset" to those points with values for f in the interval [l, u].
85  */
86 static struct isl_basic_set *add_bounds(struct isl_basic_set *bset,
87         isl_int *f, isl_int l, isl_int u)
88 {
89         int k;
90         unsigned total;
91
92         total = isl_basic_set_total_dim(bset);
93         bset = isl_basic_set_extend_constraints(bset, 0, 2);
94
95         k = isl_basic_set_alloc_inequality(bset);
96         if (k < 0)
97                 goto error;
98         isl_seq_cpy(bset->ineq[k], f, 1 + total);
99         isl_int_sub(bset->ineq[k][0], bset->ineq[k][0], l);
100
101         k = isl_basic_set_alloc_inequality(bset);
102         if (k < 0)
103                 goto error;
104         isl_seq_neg(bset->ineq[k], f, 1 + total);
105         isl_int_add(bset->ineq[k][0], bset->ineq[k][0], u);
106
107         return bset;
108 error:
109         isl_basic_set_free(bset);
110         return NULL;
111 }
112
113 /* Find an integer point in "bset" that minimizes f (in any) such that
114  * the value of f lies inside the interval [l, u].
115  * Return this integer point if it can be found.
116  * Otherwise, return sol.
117  *
118  * We perform a number of steps until l > u.
119  * In each step, we look for an integer point with value in either
120  * the whole interval [l, u] or half of the interval [l, l+floor(u-l-1/2)].
121  * The choice depends on whether we have found an integer point in the
122  * previous step.  If so, we look for the next point in half of the remaining
123  * interval.
124  * If we find a point, the current solution is updated and u is set
125  * to its value minus 1.
126  * If no point can be found, we update l to the upper bound of the interval
127  * we checked (u or l+floor(u-l-1/2)) plus 1.
128  */
129 static struct isl_vec *solve_ilp_search(struct isl_basic_set *bset,
130         isl_int *f, isl_int *opt, struct isl_vec *sol, isl_int l, isl_int u)
131 {
132         isl_int tmp;
133         int divide = 1;
134
135         isl_int_init(tmp);
136
137         while (isl_int_le(l, u)) {
138                 struct isl_basic_set *slice;
139                 struct isl_vec *sample;
140
141                 if (!divide)
142                         isl_int_set(tmp, u);
143                 else {
144                         isl_int_sub(tmp, u, l);
145                         isl_int_fdiv_q_ui(tmp, tmp, 2);
146                         isl_int_add(tmp, tmp, l);
147                 }
148                 slice = add_bounds(isl_basic_set_copy(bset), f, l, tmp);
149                 sample = isl_basic_set_sample_vec(slice);
150                 if (!sample) {
151                         isl_vec_free(sol);
152                         sol = NULL;
153                         break;
154                 }
155                 if (sample->size > 0) {
156                         isl_vec_free(sol);
157                         sol = sample;
158                         isl_seq_inner_product(f, sol->el, sol->size, opt);
159                         isl_int_sub_ui(u, *opt, 1);
160                         divide = 1;
161                 } else {
162                         isl_vec_free(sample);
163                         if (!divide)
164                                 break;
165                         isl_int_add_ui(l, tmp, 1);
166                         divide = 0;
167                 }
168         }
169
170         isl_int_clear(tmp);
171
172         return sol;
173 }
174
175 /* Find an integer point in "bset" that minimizes f (if any).
176  * If sol_p is not NULL then the integer point is returned in *sol_p.
177  * The optimal value of f is returned in *opt.
178  *
179  * The algorithm maintains a currently best solution and an interval [l, u]
180  * of values of f for which integer solutions could potentially still be found.
181  * The initial value of the best solution so far is any solution.
182  * The initial value of l is minimal value of f over the rationals
183  * (rounded up to the nearest integer).
184  * The initial value of u is the value of f at the initial solution minus 1.
185  *
186  * We then call solve_ilp_search to perform a binary search on the interval.
187  */
188 static enum isl_lp_result solve_ilp(struct isl_basic_set *bset,
189                                       isl_int *f, isl_int *opt,
190                                       struct isl_vec **sol_p)
191 {
192         enum isl_lp_result res;
193         isl_int l, u;
194         struct isl_vec *sol;
195
196         res = isl_basic_set_solve_lp(bset, 0, f, bset->ctx->one,
197                                         opt, NULL, &sol);
198         if (res == isl_lp_ok && isl_int_is_one(sol->el[0])) {
199                 if (sol_p)
200                         *sol_p = sol;
201                 else
202                         isl_vec_free(sol);
203                 return isl_lp_ok;
204         }
205         isl_vec_free(sol);
206         if (res == isl_lp_error || res == isl_lp_empty)
207                 return res;
208
209         sol = initial_solution(bset, f);
210         if (!sol)
211                 return isl_lp_error;
212         if (sol->size == 0) {
213                 isl_vec_free(sol);
214                 return isl_lp_empty;
215         }
216         if (res == isl_lp_unbounded) {
217                 isl_vec_free(sol);
218                 return isl_lp_unbounded;
219         }
220
221         isl_int_init(l);
222         isl_int_init(u);
223
224         isl_int_set(l, *opt);
225
226         isl_seq_inner_product(f, sol->el, sol->size, opt);
227         isl_int_sub_ui(u, *opt, 1);
228
229         sol = solve_ilp_search(bset, f, opt, sol, l, u);
230         if (!sol)
231                 res = isl_lp_error;
232
233         isl_int_clear(l);
234         isl_int_clear(u);
235
236         if (sol_p)
237                 *sol_p = sol;
238         else
239                 isl_vec_free(sol);
240
241         return res;
242 }
243
244 static enum isl_lp_result solve_ilp_with_eq(struct isl_basic_set *bset, int max,
245                                       isl_int *f, isl_int *opt,
246                                       struct isl_vec **sol_p)
247 {
248         unsigned dim;
249         enum isl_lp_result res;
250         struct isl_mat *T = NULL;
251         struct isl_vec *v;
252
253         bset = isl_basic_set_copy(bset);
254         dim = isl_basic_set_total_dim(bset);
255         v = isl_vec_alloc(bset->ctx, 1 + dim);
256         if (!v)
257                 goto error;
258         isl_seq_cpy(v->el, f, 1 + dim);
259         bset = isl_basic_set_remove_equalities(bset, &T, NULL);
260         v = isl_vec_mat_product(v, isl_mat_copy(T));
261         if (!v)
262                 goto error;
263         res = isl_basic_set_solve_ilp(bset, max, v->el, opt, sol_p);
264         isl_vec_free(v);
265         if (res == isl_lp_ok && sol_p) {
266                 *sol_p = isl_mat_vec_product(T, *sol_p);
267                 if (!*sol_p)
268                         res = isl_lp_error;
269         } else
270                 isl_mat_free(T);
271         isl_basic_set_free(bset);
272         return res;
273 error:
274         isl_mat_free(T);
275         isl_basic_set_free(bset);
276         return isl_lp_error;
277 }
278
279 /* Find an integer point in "bset" that minimizes (or maximizes if max is set)
280  * f (if any).
281  * If sol_p is not NULL then the integer point is returned in *sol_p.
282  * The optimal value of f is returned in *opt.
283  *
284  * If there is any equality among the points in "bset", then we first
285  * project it out.  Otherwise, we continue with solve_ilp above.
286  */
287 enum isl_lp_result isl_basic_set_solve_ilp(struct isl_basic_set *bset, int max,
288                                       isl_int *f, isl_int *opt,
289                                       struct isl_vec **sol_p)
290 {
291         unsigned dim;
292         enum isl_lp_result res;
293
294         if (!bset)
295                 return isl_lp_error;
296         if (sol_p)
297                 *sol_p = NULL;
298
299         isl_assert(bset->ctx, isl_basic_set_n_param(bset) == 0, goto error);
300
301         if (bset->n_eq)
302                 return solve_ilp_with_eq(bset, max, f, opt, sol_p);
303
304         dim = isl_basic_set_total_dim(bset);
305
306         if (max)
307                 isl_seq_neg(f, f, 1 + dim);
308
309         res = solve_ilp(bset, f, opt, sol_p);
310
311         if (max) {
312                 isl_seq_neg(f, f, 1 + dim);
313                 isl_int_neg(*opt, *opt);
314         }
315
316         return res;
317 error:
318         isl_basic_set_free(bset);
319         return isl_lp_error;
320 }