| 1 | /* |
| 2 | * Copyright 2006-2007 Universiteit Leiden |
| 3 | * Copyright 2008-2009 Katholieke Universiteit Leuven |
| 4 | * |
| 5 | * Use of this software is governed by the MIT license |
| 6 | * |
| 7 | * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science, |
| 8 | * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands |
| 9 | * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A, |
| 10 | * B-3001 Leuven, Belgium |
| 11 | */ |
| 12 | |
| 13 | #include <stdlib.h> |
| 14 | #include <isl_ctx_private.h> |
| 15 | #include <isl_map_private.h> |
| 16 | #include <isl_vec_private.h> |
| 17 | #include <isl_options_private.h> |
| 18 | #include "isl_basis_reduction.h" |
| 19 | |
| 20 | static void save_alpha(GBR_LP *lp, int first, int n, GBR_type *alpha) |
| 21 | { |
| 22 | int i; |
| 23 | |
| 24 | for (i = 0; i < n; ++i) |
| 25 | GBR_lp_get_alpha(lp, first + i, &alpha[i]); |
| 26 | } |
| 27 | |
| 28 | /* Compute a reduced basis for the set represented by the tableau "tab". |
| 29 | * tab->basis, which must be initialized by the calling function to an affine |
| 30 | * unimodular basis, is updated to reflect the reduced basis. |
| 31 | * The first tab->n_zero rows of the basis (ignoring the constant row) |
| 32 | * are assumed to correspond to equalities and are left untouched. |
| 33 | * tab->n_zero is updated to reflect any additional equalities that |
| 34 | * have been detected in the first rows of the new basis. |
| 35 | * The final tab->n_unbounded rows of the basis are assumed to correspond |
| 36 | * to unbounded directions and are also left untouched. |
| 37 | * In particular this means that the remaining rows are assumed to |
| 38 | * correspond to bounded directions. |
| 39 | * |
| 40 | * This function implements the algorithm described in |
| 41 | * "An Implementation of the Generalized Basis Reduction Algorithm |
| 42 | * for Integer Programming" of Cook el al. to compute a reduced basis. |
| 43 | * We use \epsilon = 1/4. |
| 44 | * |
| 45 | * If ctx->opt->gbr_only_first is set, the user is only interested |
| 46 | * in the first direction. In this case we stop the basis reduction when |
| 47 | * the width in the first direction becomes smaller than 2. |
| 48 | */ |
| 49 | struct isl_tab *isl_tab_compute_reduced_basis(struct isl_tab *tab) |
| 50 | { |
| 51 | unsigned dim; |
| 52 | struct isl_ctx *ctx; |
| 53 | struct isl_mat *B; |
| 54 | int i; |
| 55 | GBR_LP *lp = NULL; |
| 56 | GBR_type F_old, alpha, F_new; |
| 57 | int row; |
| 58 | isl_int tmp; |
| 59 | struct isl_vec *b_tmp; |
| 60 | GBR_type *F = NULL; |
| 61 | GBR_type *alpha_buffer[2] = { NULL, NULL }; |
| 62 | GBR_type *alpha_saved; |
| 63 | GBR_type F_saved; |
| 64 | int use_saved = 0; |
| 65 | isl_int mu[2]; |
| 66 | GBR_type mu_F[2]; |
| 67 | GBR_type two; |
| 68 | GBR_type one; |
| 69 | int empty = 0; |
| 70 | int fixed = 0; |
| 71 | int fixed_saved = 0; |
| 72 | int mu_fixed[2]; |
| 73 | int n_bounded; |
| 74 | int gbr_only_first; |
| 75 | |
| 76 | if (!tab) |
| 77 | return NULL; |
| 78 | |
| 79 | if (tab->empty) |
| 80 | return tab; |
| 81 | |
| 82 | ctx = tab->mat->ctx; |
| 83 | gbr_only_first = ctx->opt->gbr_only_first; |
| 84 | dim = tab->n_var; |
| 85 | B = tab->basis; |
| 86 | if (!B) |
| 87 | return tab; |
| 88 | |
| 89 | n_bounded = dim - tab->n_unbounded; |
| 90 | if (n_bounded <= tab->n_zero + 1) |
| 91 | return tab; |
| 92 | |
| 93 | isl_int_init(tmp); |
| 94 | isl_int_init(mu[0]); |
| 95 | isl_int_init(mu[1]); |
| 96 | |
| 97 | GBR_init(alpha); |
| 98 | GBR_init(F_old); |
| 99 | GBR_init(F_new); |
| 100 | GBR_init(F_saved); |
| 101 | GBR_init(mu_F[0]); |
| 102 | GBR_init(mu_F[1]); |
| 103 | GBR_init(two); |
| 104 | GBR_init(one); |
| 105 | |
| 106 | b_tmp = isl_vec_alloc(ctx, size: dim); |
| 107 | if (!b_tmp) |
| 108 | goto error; |
| 109 | |
| 110 | F = isl_alloc_array(ctx, GBR_type, n_bounded); |
| 111 | alpha_buffer[0] = isl_alloc_array(ctx, GBR_type, n_bounded); |
| 112 | alpha_buffer[1] = isl_alloc_array(ctx, GBR_type, n_bounded); |
| 113 | alpha_saved = alpha_buffer[0]; |
| 114 | |
| 115 | if (!F || !alpha_buffer[0] || !alpha_buffer[1]) |
| 116 | goto error; |
| 117 | |
| 118 | for (i = 0; i < n_bounded; ++i) { |
| 119 | GBR_init(F[i]); |
| 120 | GBR_init(alpha_buffer[0][i]); |
| 121 | GBR_init(alpha_buffer[1][i]); |
| 122 | } |
| 123 | |
| 124 | GBR_set_ui(two, 2); |
| 125 | GBR_set_ui(one, 1); |
| 126 | |
| 127 | lp = GBR_lp_init(tab); |
| 128 | if (!lp) |
| 129 | goto error; |
| 130 | |
| 131 | i = tab->n_zero; |
| 132 | |
| 133 | GBR_lp_set_obj(lp, B->row[1+i]+1, dim); |
| 134 | ctx->stats->gbr_solved_lps++; |
| 135 | if (GBR_lp_solve(lp) < 0) |
| 136 | goto error; |
| 137 | GBR_lp_get_obj_val(lp, &F[i]); |
| 138 | |
| 139 | if (GBR_lt(F[i], one)) { |
| 140 | if (!GBR_is_zero(F[i])) { |
| 141 | empty = GBR_lp_cut(lp, B->row[1+i]+1); |
| 142 | if (empty) |
| 143 | goto done; |
| 144 | GBR_set_ui(F[i], 0); |
| 145 | } |
| 146 | tab->n_zero++; |
| 147 | } |
| 148 | |
| 149 | do { |
| 150 | if (i+1 == tab->n_zero) { |
| 151 | GBR_lp_set_obj(lp, B->row[1+i+1]+1, dim); |
| 152 | ctx->stats->gbr_solved_lps++; |
| 153 | if (GBR_lp_solve(lp) < 0) |
| 154 | goto error; |
| 155 | GBR_lp_get_obj_val(lp, &F_new); |
| 156 | fixed = GBR_lp_is_fixed(lp); |
| 157 | GBR_set_ui(alpha, 0); |
| 158 | } else if (use_saved) { |
| 159 | row = GBR_lp_next_row(lp); |
| 160 | GBR_set(F_new, F_saved); |
| 161 | fixed = fixed_saved; |
| 162 | GBR_set(alpha, alpha_saved[i]); |
| 163 | } else { |
| 164 | row = GBR_lp_add_row(lp, B->row[1+i]+1, dim); |
| 165 | GBR_lp_set_obj(lp, B->row[1+i+1]+1, dim); |
| 166 | ctx->stats->gbr_solved_lps++; |
| 167 | if (GBR_lp_solve(lp) < 0) |
| 168 | goto error; |
| 169 | GBR_lp_get_obj_val(lp, &F_new); |
| 170 | fixed = GBR_lp_is_fixed(lp); |
| 171 | |
| 172 | GBR_lp_get_alpha(lp, row, &alpha); |
| 173 | |
| 174 | if (i > 0) |
| 175 | save_alpha(lp, first: row-i, n: i, alpha: alpha_saved); |
| 176 | |
| 177 | if (GBR_lp_del_row(lp) < 0) |
| 178 | goto error; |
| 179 | } |
| 180 | GBR_set(F[i+1], F_new); |
| 181 | |
| 182 | GBR_floor(mu[0], alpha); |
| 183 | GBR_ceil(mu[1], alpha); |
| 184 | |
| 185 | if (isl_int_eq(mu[0], mu[1])) |
| 186 | isl_int_set(tmp, mu[0]); |
| 187 | else { |
| 188 | int j; |
| 189 | |
| 190 | for (j = 0; j <= 1; ++j) { |
| 191 | isl_int_set(tmp, mu[j]); |
| 192 | isl_seq_combine(dst: b_tmp->el, |
| 193 | m1: ctx->one, src1: B->row[1+i+1]+1, |
| 194 | m2: tmp, src2: B->row[1+i]+1, len: dim); |
| 195 | GBR_lp_set_obj(lp, b_tmp->el, dim); |
| 196 | ctx->stats->gbr_solved_lps++; |
| 197 | if (GBR_lp_solve(lp) < 0) |
| 198 | goto error; |
| 199 | GBR_lp_get_obj_val(lp, &mu_F[j]); |
| 200 | mu_fixed[j] = GBR_lp_is_fixed(lp); |
| 201 | if (i > 0) |
| 202 | save_alpha(lp, first: row-i, n: i, alpha: alpha_buffer[j]); |
| 203 | } |
| 204 | |
| 205 | if (GBR_lt(mu_F[0], mu_F[1])) |
| 206 | j = 0; |
| 207 | else |
| 208 | j = 1; |
| 209 | |
| 210 | isl_int_set(tmp, mu[j]); |
| 211 | GBR_set(F_new, mu_F[j]); |
| 212 | fixed = mu_fixed[j]; |
| 213 | alpha_saved = alpha_buffer[j]; |
| 214 | } |
| 215 | isl_seq_combine(dst: B->row[1+i+1]+1, m1: ctx->one, src1: B->row[1+i+1]+1, |
| 216 | m2: tmp, src2: B->row[1+i]+1, len: dim); |
| 217 | |
| 218 | if (i+1 == tab->n_zero && fixed) { |
| 219 | if (!GBR_is_zero(F[i+1])) { |
| 220 | empty = GBR_lp_cut(lp, B->row[1+i+1]+1); |
| 221 | if (empty) |
| 222 | goto done; |
| 223 | GBR_set_ui(F[i+1], 0); |
| 224 | } |
| 225 | tab->n_zero++; |
| 226 | } |
| 227 | |
| 228 | GBR_set(F_old, F[i]); |
| 229 | |
| 230 | use_saved = 0; |
| 231 | /* mu_F[0] = 4 * F_new; mu_F[1] = 3 * F_old */ |
| 232 | GBR_set_ui(mu_F[0], 4); |
| 233 | GBR_mul(mu_F[0], mu_F[0], F_new); |
| 234 | GBR_set_ui(mu_F[1], 3); |
| 235 | GBR_mul(mu_F[1], mu_F[1], F_old); |
| 236 | if (GBR_lt(mu_F[0], mu_F[1])) { |
| 237 | B = isl_mat_swap_rows(mat: B, i: 1 + i, j: 1 + i + 1); |
| 238 | if (i > tab->n_zero) { |
| 239 | use_saved = 1; |
| 240 | GBR_set(F_saved, F_new); |
| 241 | fixed_saved = fixed; |
| 242 | if (GBR_lp_del_row(lp) < 0) |
| 243 | goto error; |
| 244 | --i; |
| 245 | } else { |
| 246 | GBR_set(F[tab->n_zero], F_new); |
| 247 | if (gbr_only_first && GBR_lt(F[tab->n_zero], two)) |
| 248 | break; |
| 249 | |
| 250 | if (fixed) { |
| 251 | if (!GBR_is_zero(F[tab->n_zero])) { |
| 252 | empty = GBR_lp_cut(lp, B->row[1+tab->n_zero]+1); |
| 253 | if (empty) |
| 254 | goto done; |
| 255 | GBR_set_ui(F[tab->n_zero], 0); |
| 256 | } |
| 257 | tab->n_zero++; |
| 258 | } |
| 259 | } |
| 260 | } else { |
| 261 | GBR_lp_add_row(lp, B->row[1+i]+1, dim); |
| 262 | ++i; |
| 263 | } |
| 264 | } while (i < n_bounded - 1); |
| 265 | |
| 266 | if (0) { |
| 267 | done: |
| 268 | if (empty < 0) { |
| 269 | error: |
| 270 | isl_mat_free(mat: B); |
| 271 | B = NULL; |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | GBR_lp_delete(lp); |
| 276 | |
| 277 | if (alpha_buffer[1]) |
| 278 | for (i = 0; i < n_bounded; ++i) { |
| 279 | GBR_clear(F[i]); |
| 280 | GBR_clear(alpha_buffer[0][i]); |
| 281 | GBR_clear(alpha_buffer[1][i]); |
| 282 | } |
| 283 | free(ptr: F); |
| 284 | free(ptr: alpha_buffer[0]); |
| 285 | free(ptr: alpha_buffer[1]); |
| 286 | |
| 287 | isl_vec_free(vec: b_tmp); |
| 288 | |
| 289 | GBR_clear(alpha); |
| 290 | GBR_clear(F_old); |
| 291 | GBR_clear(F_new); |
| 292 | GBR_clear(F_saved); |
| 293 | GBR_clear(mu_F[0]); |
| 294 | GBR_clear(mu_F[1]); |
| 295 | GBR_clear(two); |
| 296 | GBR_clear(one); |
| 297 | |
| 298 | isl_int_clear(tmp); |
| 299 | isl_int_clear(mu[0]); |
| 300 | isl_int_clear(mu[1]); |
| 301 | |
| 302 | tab->basis = B; |
| 303 | |
| 304 | return tab; |
| 305 | } |
| 306 | |
| 307 | /* Compute an affine form of a reduced basis of the given basic |
| 308 | * non-parametric set, which is assumed to be bounded and not |
| 309 | * include any integer divisions. |
| 310 | * The first column and the first row correspond to the constant term. |
| 311 | * |
| 312 | * If the input contains any equalities, we first create an initial |
| 313 | * basis with the equalities first. Otherwise, we start off with |
| 314 | * the identity matrix. |
| 315 | */ |
| 316 | __isl_give isl_mat *isl_basic_set_reduced_basis(__isl_keep isl_basic_set *bset) |
| 317 | { |
| 318 | struct isl_mat *basis; |
| 319 | struct isl_tab *tab; |
| 320 | |
| 321 | if (isl_basic_set_check_no_locals(bset) < 0 || |
| 322 | isl_basic_set_check_no_params(bset) < 0) |
| 323 | return NULL; |
| 324 | |
| 325 | tab = isl_tab_from_basic_set(bset, track: 0); |
| 326 | if (!tab) |
| 327 | return NULL; |
| 328 | |
| 329 | if (bset->n_eq == 0) |
| 330 | tab->basis = isl_mat_identity(ctx: bset->ctx, n_row: 1 + tab->n_var); |
| 331 | else { |
| 332 | isl_mat *eq; |
| 333 | isl_size nvar = isl_basic_set_dim(bset, type: isl_dim_all); |
| 334 | if (nvar < 0) |
| 335 | goto error; |
| 336 | eq = isl_mat_sub_alloc6(ctx: bset->ctx, row: bset->eq, first_row: 0, n_row: bset->n_eq, |
| 337 | first_col: 1, n_col: nvar); |
| 338 | eq = isl_mat_left_hermite(M: eq, neg: 0, NULL, Q: &tab->basis); |
| 339 | tab->basis = isl_mat_lin_to_aff(mat: tab->basis); |
| 340 | tab->n_zero = bset->n_eq; |
| 341 | isl_mat_free(mat: eq); |
| 342 | } |
| 343 | tab = isl_tab_compute_reduced_basis(tab); |
| 344 | if (!tab) |
| 345 | return NULL; |
| 346 | |
| 347 | basis = isl_mat_copy(mat: tab->basis); |
| 348 | |
| 349 | isl_tab_free(tab); |
| 350 | |
| 351 | return basis; |
| 352 | error: |
| 353 | isl_tab_free(tab); |
| 354 | return NULL; |
| 355 | } |
| 356 | |