add generalized basis reduction based ILP solver
[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(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 (if any).
114  * If sol_p is not NULL then the integer point is returned in *sol_p.
115  * The optimal value of f is returned in *opt.
116  *
117  * The algorithm maintains a currently best solution and an interval [l, u]
118  * of values of f for which integer solutions could potentially still be found.
119  * The initial value of the best solution so far is any solution.
120  * The initial value of l is minimal value of f over the rationals
121  * (rounded up to the nearest integer).
122  * The initial value of u is the value of f at the current solution minus 1.
123  *
124  * We perform a number of steps until l > u.
125  * In each step, we look for an integer point with value in either
126  * the whole interval [l, u] or half of the interval [l, l+floor(u-l-1/2)].
127  * The choice depends on whether we have found an integer point in the
128  * previous step.  If so, we look for the next point in half of the remaining
129  * interval.
130  * If we find a point, the current solution is updated and u is set
131  * to its value minus 1.
132  * If no point can be found, we update l to the upper bound of the interval
133  * we checked (u or l+floor(u-l-1/2)) plus 1.
134  */
135 static enum isl_lp_result solve_ilp(struct isl_basic_set *bset,
136                                       isl_int *f, isl_int *opt,
137                                       struct isl_vec **sol_p)
138 {
139         enum isl_lp_result res;
140         isl_int l, u, tmp;
141         struct isl_vec *sol;
142         int divide = 1;
143
144         res = isl_basic_set_solve_lp(bset, 0, f, bset->ctx->one,
145                                         opt, NULL, &sol);
146         if (res == isl_lp_ok && isl_int_is_one(sol->el[0])) {
147                 if (sol_p)
148                         *sol_p = sol;
149                 else
150                         isl_vec_free(sol);
151                 return isl_lp_ok;
152         }
153         isl_vec_free(sol);
154         if (res == isl_lp_error || res == isl_lp_empty)
155                 return res;
156
157         sol = initial_solution(bset, f);
158         if (!sol)
159                 return isl_lp_error;
160         if (sol->size == 0) {
161                 isl_vec_free(sol);
162                 return isl_lp_empty;
163         }
164         if (res == isl_lp_unbounded) {
165                 isl_vec_free(sol);
166                 return isl_lp_unbounded;
167         }
168
169         isl_int_init(l);
170         isl_int_init(u);
171         isl_int_init(tmp);
172
173         isl_int_set(l, *opt);
174
175         isl_seq_inner_product(f, sol->el, sol->size, opt);
176         isl_int_sub_ui(u, *opt, 1);
177
178         while (isl_int_le(l, u)) {
179                 struct isl_basic_set *slice;
180                 struct isl_vec *sample;
181
182                 if (!divide)
183                         isl_int_set(tmp, u);
184                 else {
185                         isl_int_sub(tmp, u, l);
186                         isl_int_fdiv_q_ui(tmp, tmp, 2);
187                         isl_int_add(tmp, tmp, l);
188                 }
189                 slice = add_bounds(isl_basic_set_copy(bset), f, l, tmp);
190                 sample = isl_basic_set_sample(slice);
191                 if (!sample) {
192                         isl_vec_free(sol);
193                         sol = NULL;
194                         res = isl_lp_error;
195                         break;
196                 }
197                 if (sample->size > 0) {
198                         isl_vec_free(sol);
199                         sol = sample;
200                         isl_seq_inner_product(f, sol->el, sol->size, opt);
201                         isl_int_sub_ui(u, *opt, 1);
202                         divide = 1;
203                 } else {
204                         isl_vec_free(sample);
205                         if (!divide)
206                                 break;
207                         isl_int_add_ui(l, tmp, 1);
208                         divide = 0;
209                 }
210         }
211
212         isl_int_clear(l);
213         isl_int_clear(u);
214         isl_int_clear(tmp);
215
216         if (sol_p)
217                 *sol_p = sol;
218         else
219                 isl_vec_free(sol);
220
221         return res;
222 }
223
224 enum isl_lp_result solve_ilp_with_eq(struct isl_basic_set *bset, int max,
225                                       isl_int *f, isl_int *opt,
226                                       struct isl_vec **sol_p)
227 {
228         unsigned dim;
229         enum isl_lp_result res;
230         struct isl_mat *T = NULL;
231         struct isl_vec *v;
232
233         dim = isl_basic_set_total_dim(bset);
234         v = isl_vec_alloc(bset->ctx, 1 + dim);
235         if (!v)
236                 goto error;
237         isl_seq_cpy(v->el, f, 1 + dim);
238         bset = isl_basic_set_remove_equalities(bset, &T, NULL);
239         v = isl_vec_mat_product(v, isl_mat_copy(T));
240         if (!v)
241                 goto error;
242         res = isl_basic_set_solve_ilp(bset, max, v->el, opt, sol_p);
243         isl_vec_free(v);
244         if (res == isl_lp_ok && *sol_p) {
245                 *sol_p = isl_mat_vec_product(T, *sol_p);
246                 if (!*sol_p)
247                         res = isl_lp_error;
248         } else
249                 isl_mat_free(T);
250         return res;
251 error:
252         isl_mat_free(T);
253         isl_basic_set_free(bset);
254         return isl_lp_error;
255 }
256
257 /* Find an integer point in "bset" that minimizes (or maximizes if max is set)
258  * f (if any).
259  * If sol_p is not NULL then the integer point is returned in *sol_p.
260  * The optimal value of f is returned in *opt.
261  *
262  * If there is any equality among the points in "bset", then we first
263  * project it out.  Otherwise, we continue with solve_ilp above.
264  */
265 enum isl_lp_result isl_basic_set_solve_ilp(struct isl_basic_set *bset, int max,
266                                       isl_int *f, isl_int *opt,
267                                       struct isl_vec **sol_p)
268 {
269         unsigned dim;
270         enum isl_lp_result res;
271
272         if (!bset)
273                 return isl_lp_error;
274         if (sol_p)
275                 *sol_p = NULL;
276
277         isl_assert(bset->ctx, isl_basic_set_n_param(bset) == 0, goto error);
278
279         if (bset->n_eq)
280                 return solve_ilp_with_eq(bset, max, f, opt, sol_p);
281
282         dim = isl_basic_set_total_dim(bset);
283
284         if (max)
285                 isl_seq_neg(f, f, 1 + dim);
286
287         res = solve_ilp(bset, f, opt, sol_p);
288
289         if (max) {
290                 isl_seq_neg(f, f, 1 + dim);
291                 isl_int_neg(*opt, *opt);
292         }
293
294         return res;
295 error:
296         isl_basic_set_free(bset);
297         return isl_lp_error;
298 }