| 1 | /* |
| 2 | * Copyright 2008-2009 Katholieke Universiteit Leuven |
| 3 | * Copyright 2010 INRIA Saclay |
| 4 | * |
| 5 | * Use of this software is governed by the MIT license |
| 6 | * |
| 7 | * Written by Sven Verdoolaege, K.U.Leuven, Departement |
| 8 | * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium |
| 9 | * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite, |
| 10 | * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France |
| 11 | */ |
| 12 | |
| 13 | #include <isl_mat_private.h> |
| 14 | #include <isl_vec_private.h> |
| 15 | #include <isl_seq.h> |
| 16 | #include "isl_map_private.h" |
| 17 | #include "isl_equalities.h" |
| 18 | #include <isl_val_private.h> |
| 19 | |
| 20 | /* Given a set of modulo constraints |
| 21 | * |
| 22 | * c + A y = 0 mod d |
| 23 | * |
| 24 | * this function computes a particular solution y_0 |
| 25 | * |
| 26 | * The input is given as a matrix B = [ c A ] and a vector d. |
| 27 | * |
| 28 | * The output is matrix containing the solution y_0 or |
| 29 | * a zero-column matrix if the constraints admit no integer solution. |
| 30 | * |
| 31 | * The given set of constrains is equivalent to |
| 32 | * |
| 33 | * c + A y = -D x |
| 34 | * |
| 35 | * with D = diag d and x a fresh set of variables. |
| 36 | * Reducing both c and A modulo d does not change the |
| 37 | * value of y in the solution and may lead to smaller coefficients. |
| 38 | * Let M = [ D A ] and [ H 0 ] = M U, the Hermite normal form of M. |
| 39 | * Then |
| 40 | * [ x ] |
| 41 | * M [ y ] = - c |
| 42 | * and so |
| 43 | * [ x ] |
| 44 | * [ H 0 ] U^{-1} [ y ] = - c |
| 45 | * Let |
| 46 | * [ A ] [ x ] |
| 47 | * [ B ] = U^{-1} [ y ] |
| 48 | * then |
| 49 | * H A + 0 B = -c |
| 50 | * |
| 51 | * so B may be chosen arbitrarily, e.g., B = 0, and then |
| 52 | * |
| 53 | * [ x ] = [ -c ] |
| 54 | * U^{-1} [ y ] = [ 0 ] |
| 55 | * or |
| 56 | * [ x ] [ -c ] |
| 57 | * [ y ] = U [ 0 ] |
| 58 | * specifically, |
| 59 | * |
| 60 | * y = U_{2,1} (-c) |
| 61 | * |
| 62 | * If any of the coordinates of this y are non-integer |
| 63 | * then the constraints admit no integer solution and |
| 64 | * a zero-column matrix is returned. |
| 65 | */ |
| 66 | static __isl_give isl_mat *particular_solution(__isl_keep isl_mat *B, |
| 67 | __isl_keep isl_vec *d) |
| 68 | { |
| 69 | int i, j; |
| 70 | struct isl_mat *M = NULL; |
| 71 | struct isl_mat *C = NULL; |
| 72 | struct isl_mat *U = NULL; |
| 73 | struct isl_mat *H = NULL; |
| 74 | struct isl_mat *cst = NULL; |
| 75 | struct isl_mat *T = NULL; |
| 76 | |
| 77 | M = isl_mat_alloc(ctx: B->ctx, n_row: B->n_row, n_col: B->n_row + B->n_col - 1); |
| 78 | C = isl_mat_alloc(ctx: B->ctx, n_row: 1 + B->n_row, n_col: 1); |
| 79 | if (!M || !C) |
| 80 | goto error; |
| 81 | isl_int_set_si(C->row[0][0], 1); |
| 82 | for (i = 0; i < B->n_row; ++i) { |
| 83 | isl_seq_clr(p: M->row[i], len: B->n_row); |
| 84 | isl_int_set(M->row[i][i], d->block.data[i]); |
| 85 | isl_int_neg(C->row[1 + i][0], B->row[i][0]); |
| 86 | isl_int_fdiv_r(C->row[1+i][0], C->row[1+i][0], M->row[i][i]); |
| 87 | for (j = 0; j < B->n_col - 1; ++j) |
| 88 | isl_int_fdiv_r(M->row[i][B->n_row + j], |
| 89 | B->row[i][1 + j], M->row[i][i]); |
| 90 | } |
| 91 | M = isl_mat_left_hermite(M, neg: 0, U: &U, NULL); |
| 92 | if (!M || !U) |
| 93 | goto error; |
| 94 | H = isl_mat_sub_alloc(mat: M, first_row: 0, n_row: B->n_row, first_col: 0, n_col: B->n_row); |
| 95 | H = isl_mat_lin_to_aff(mat: H); |
| 96 | C = isl_mat_inverse_product(left: H, right: C); |
| 97 | if (!C) |
| 98 | goto error; |
| 99 | for (i = 0; i < B->n_row; ++i) { |
| 100 | if (!isl_int_is_divisible_by(C->row[1+i][0], C->row[0][0])) |
| 101 | break; |
| 102 | isl_int_divexact(C->row[1+i][0], C->row[1+i][0], C->row[0][0]); |
| 103 | } |
| 104 | if (i < B->n_row) |
| 105 | cst = isl_mat_alloc(ctx: B->ctx, n_row: B->n_row, n_col: 0); |
| 106 | else |
| 107 | cst = isl_mat_sub_alloc(mat: C, first_row: 1, n_row: B->n_row, first_col: 0, n_col: 1); |
| 108 | T = isl_mat_sub_alloc(mat: U, first_row: B->n_row, n_row: B->n_col - 1, first_col: 0, n_col: B->n_row); |
| 109 | cst = isl_mat_product(left: T, right: cst); |
| 110 | isl_mat_free(mat: M); |
| 111 | isl_mat_free(mat: C); |
| 112 | isl_mat_free(mat: U); |
| 113 | return cst; |
| 114 | error: |
| 115 | isl_mat_free(mat: M); |
| 116 | isl_mat_free(mat: C); |
| 117 | isl_mat_free(mat: U); |
| 118 | return NULL; |
| 119 | } |
| 120 | |
| 121 | /* Compute and return the matrix |
| 122 | * |
| 123 | * U_1^{-1} diag(d_1, 1, ..., 1) |
| 124 | * |
| 125 | * with U_1 the unimodular completion of the first (and only) row of B. |
| 126 | * The columns of this matrix generate the lattice that satisfies |
| 127 | * the single (linear) modulo constraint. |
| 128 | */ |
| 129 | static __isl_take isl_mat *parameter_compression_1(__isl_keep isl_mat *B, |
| 130 | __isl_keep isl_vec *d) |
| 131 | { |
| 132 | struct isl_mat *U; |
| 133 | |
| 134 | U = isl_mat_alloc(ctx: B->ctx, n_row: B->n_col - 1, n_col: B->n_col - 1); |
| 135 | if (!U) |
| 136 | return NULL; |
| 137 | isl_seq_cpy(dst: U->row[0], src: B->row[0] + 1, len: B->n_col - 1); |
| 138 | U = isl_mat_unimodular_complete(M: U, row: 1); |
| 139 | U = isl_mat_right_inverse(mat: U); |
| 140 | if (!U) |
| 141 | return NULL; |
| 142 | isl_mat_col_mul(mat: U, dst_col: 0, f: d->block.data[0], src_col: 0); |
| 143 | U = isl_mat_lin_to_aff(mat: U); |
| 144 | return U; |
| 145 | } |
| 146 | |
| 147 | /* Compute a common lattice of solutions to the linear modulo |
| 148 | * constraints specified by B and d. |
| 149 | * See also the documentation of isl_mat_parameter_compression. |
| 150 | * We put the matrix |
| 151 | * |
| 152 | * A = [ L_1^{-T} L_2^{-T} ... L_k^{-T} ] |
| 153 | * |
| 154 | * on a common denominator. This denominator D is the lcm of modulos d. |
| 155 | * Since L_i = U_i^{-1} diag(d_i, 1, ... 1), we have |
| 156 | * L_i^{-T} = U_i^T diag(d_i, 1, ... 1)^{-T} = U_i^T diag(1/d_i, 1, ..., 1). |
| 157 | * Putting this on the common denominator, we have |
| 158 | * D * L_i^{-T} = U_i^T diag(D/d_i, D, ..., D). |
| 159 | */ |
| 160 | static __isl_give isl_mat *parameter_compression_multi(__isl_keep isl_mat *B, |
| 161 | __isl_keep isl_vec *d) |
| 162 | { |
| 163 | int i, j, k; |
| 164 | isl_int D; |
| 165 | struct isl_mat *A = NULL, *U = NULL; |
| 166 | struct isl_mat *T; |
| 167 | unsigned size; |
| 168 | |
| 169 | isl_int_init(D); |
| 170 | |
| 171 | isl_vec_lcm(vec: d, lcm: &D); |
| 172 | |
| 173 | size = B->n_col - 1; |
| 174 | A = isl_mat_alloc(ctx: B->ctx, n_row: size, n_col: B->n_row * size); |
| 175 | U = isl_mat_alloc(ctx: B->ctx, n_row: size, n_col: size); |
| 176 | if (!U || !A) |
| 177 | goto error; |
| 178 | for (i = 0; i < B->n_row; ++i) { |
| 179 | isl_seq_cpy(dst: U->row[0], src: B->row[i] + 1, len: size); |
| 180 | U = isl_mat_unimodular_complete(M: U, row: 1); |
| 181 | if (!U) |
| 182 | goto error; |
| 183 | isl_int_divexact(D, D, d->block.data[i]); |
| 184 | for (k = 0; k < U->n_col; ++k) |
| 185 | isl_int_mul(A->row[k][i*size+0], D, U->row[0][k]); |
| 186 | isl_int_mul(D, D, d->block.data[i]); |
| 187 | for (j = 1; j < U->n_row; ++j) |
| 188 | for (k = 0; k < U->n_col; ++k) |
| 189 | isl_int_mul(A->row[k][i*size+j], |
| 190 | D, U->row[j][k]); |
| 191 | } |
| 192 | A = isl_mat_left_hermite(M: A, neg: 0, NULL, NULL); |
| 193 | T = isl_mat_sub_alloc(mat: A, first_row: 0, n_row: A->n_row, first_col: 0, n_col: A->n_row); |
| 194 | T = isl_mat_lin_to_aff(mat: T); |
| 195 | if (!T) |
| 196 | goto error; |
| 197 | isl_int_set(T->row[0][0], D); |
| 198 | T = isl_mat_right_inverse(mat: T); |
| 199 | if (!T) |
| 200 | goto error; |
| 201 | isl_assert(T->ctx, isl_int_is_one(T->row[0][0]), goto error); |
| 202 | T = isl_mat_transpose(mat: T); |
| 203 | isl_mat_free(mat: A); |
| 204 | isl_mat_free(mat: U); |
| 205 | |
| 206 | isl_int_clear(D); |
| 207 | return T; |
| 208 | error: |
| 209 | isl_mat_free(mat: A); |
| 210 | isl_mat_free(mat: U); |
| 211 | isl_int_clear(D); |
| 212 | return NULL; |
| 213 | } |
| 214 | |
| 215 | /* Given a set of modulo constraints |
| 216 | * |
| 217 | * c + A y = 0 mod d |
| 218 | * |
| 219 | * this function returns an affine transformation T, |
| 220 | * |
| 221 | * y = T y' |
| 222 | * |
| 223 | * that bijectively maps the integer vectors y' to integer |
| 224 | * vectors y that satisfy the modulo constraints. |
| 225 | * |
| 226 | * This function is inspired by Section 2.5.3 |
| 227 | * of B. Meister, "Stating and Manipulating Periodicity in the Polytope |
| 228 | * Model. Applications to Program Analysis and Optimization". |
| 229 | * However, the implementation only follows the algorithm of that |
| 230 | * section for computing a particular solution and not for computing |
| 231 | * a general homogeneous solution. The latter is incomplete and |
| 232 | * may remove some valid solutions. |
| 233 | * Instead, we use an adaptation of the algorithm in Section 7 of |
| 234 | * B. Meister, S. Verdoolaege, "Polynomial Approximations in the Polytope |
| 235 | * Model: Bringing the Power of Quasi-Polynomials to the Masses". |
| 236 | * |
| 237 | * The input is given as a matrix B = [ c A ] and a vector d. |
| 238 | * Each element of the vector d corresponds to a row in B. |
| 239 | * The output is a lower triangular matrix. |
| 240 | * If no integer vector y satisfies the given constraints then |
| 241 | * a matrix with zero columns is returned. |
| 242 | * |
| 243 | * We first compute a particular solution y_0 to the given set of |
| 244 | * modulo constraints in particular_solution. If no such solution |
| 245 | * exists, then we return a zero-columned transformation matrix. |
| 246 | * Otherwise, we compute the generic solution to |
| 247 | * |
| 248 | * A y = 0 mod d |
| 249 | * |
| 250 | * That is we want to compute G such that |
| 251 | * |
| 252 | * y = G y'' |
| 253 | * |
| 254 | * with y'' integer, describes the set of solutions. |
| 255 | * |
| 256 | * We first remove the common factors of each row. |
| 257 | * In particular if gcd(A_i,d_i) != 1, then we divide the whole |
| 258 | * row i (including d_i) by this common factor. If afterwards gcd(A_i) != 1, |
| 259 | * then we divide this row of A by the common factor, unless gcd(A_i) = 0. |
| 260 | * In the later case, we simply drop the row (in both A and d). |
| 261 | * |
| 262 | * If there are no rows left in A, then G is the identity matrix. Otherwise, |
| 263 | * for each row i, we now determine the lattice of integer vectors |
| 264 | * that satisfies this row. Let U_i be the unimodular extension of the |
| 265 | * row A_i. This unimodular extension exists because gcd(A_i) = 1. |
| 266 | * The first component of |
| 267 | * |
| 268 | * y' = U_i y |
| 269 | * |
| 270 | * needs to be a multiple of d_i. Let y' = diag(d_i, 1, ..., 1) y''. |
| 271 | * Then, |
| 272 | * |
| 273 | * y = U_i^{-1} diag(d_i, 1, ..., 1) y'' |
| 274 | * |
| 275 | * for arbitrary integer vectors y''. That is, y belongs to the lattice |
| 276 | * generated by the columns of L_i = U_i^{-1} diag(d_i, 1, ..., 1). |
| 277 | * If there is only one row, then G = L_1. |
| 278 | * |
| 279 | * If there is more than one row left, we need to compute the intersection |
| 280 | * of the lattices. That is, we need to compute an L such that |
| 281 | * |
| 282 | * L = L_i L_i' for all i |
| 283 | * |
| 284 | * with L_i' some integer matrices. Let A be constructed as follows |
| 285 | * |
| 286 | * A = [ L_1^{-T} L_2^{-T} ... L_k^{-T} ] |
| 287 | * |
| 288 | * and computed the Hermite Normal Form of A = [ H 0 ] U |
| 289 | * Then, |
| 290 | * |
| 291 | * L_i^{-T} = H U_{1,i} |
| 292 | * |
| 293 | * or |
| 294 | * |
| 295 | * H^{-T} = L_i U_{1,i}^T |
| 296 | * |
| 297 | * In other words G = L = H^{-T}. |
| 298 | * To ensure that G is lower triangular, we compute and use its Hermite |
| 299 | * normal form. |
| 300 | * |
| 301 | * The affine transformation matrix returned is then |
| 302 | * |
| 303 | * [ 1 0 ] |
| 304 | * [ y_0 G ] |
| 305 | * |
| 306 | * as any y = y_0 + G y' with y' integer is a solution to the original |
| 307 | * modulo constraints. |
| 308 | */ |
| 309 | __isl_give isl_mat *isl_mat_parameter_compression(__isl_take isl_mat *B, |
| 310 | __isl_take isl_vec *d) |
| 311 | { |
| 312 | int i; |
| 313 | struct isl_mat *cst = NULL; |
| 314 | struct isl_mat *T = NULL; |
| 315 | isl_int D; |
| 316 | |
| 317 | if (!B || !d) |
| 318 | goto error; |
| 319 | isl_assert(B->ctx, B->n_row == d->size, goto error); |
| 320 | cst = particular_solution(B, d); |
| 321 | if (!cst) |
| 322 | goto error; |
| 323 | if (cst->n_col == 0) { |
| 324 | T = isl_mat_alloc(ctx: B->ctx, n_row: B->n_col, n_col: 0); |
| 325 | isl_mat_free(mat: cst); |
| 326 | isl_mat_free(mat: B); |
| 327 | isl_vec_free(vec: d); |
| 328 | return T; |
| 329 | } |
| 330 | isl_int_init(D); |
| 331 | /* Replace a*g*row = 0 mod g*m by row = 0 mod m */ |
| 332 | for (i = 0; i < B->n_row; ++i) { |
| 333 | isl_seq_gcd(p: B->row[i] + 1, len: B->n_col - 1, gcd: &D); |
| 334 | if (isl_int_is_one(D)) |
| 335 | continue; |
| 336 | if (isl_int_is_zero(D)) { |
| 337 | B = isl_mat_drop_rows(mat: B, row: i, n: 1); |
| 338 | d = isl_vec_cow(vec: d); |
| 339 | if (!B || !d) |
| 340 | goto error2; |
| 341 | isl_seq_cpy(dst: d->block.data+i, src: d->block.data+i+1, |
| 342 | len: d->size - (i+1)); |
| 343 | d->size--; |
| 344 | i--; |
| 345 | continue; |
| 346 | } |
| 347 | B = isl_mat_cow(mat: B); |
| 348 | if (!B) |
| 349 | goto error2; |
| 350 | isl_seq_scale_down(dst: B->row[i] + 1, src: B->row[i] + 1, f: D, len: B->n_col-1); |
| 351 | isl_int_gcd(D, D, d->block.data[i]); |
| 352 | d = isl_vec_cow(vec: d); |
| 353 | if (!d) |
| 354 | goto error2; |
| 355 | isl_int_divexact(d->block.data[i], d->block.data[i], D); |
| 356 | } |
| 357 | isl_int_clear(D); |
| 358 | if (B->n_row == 0) |
| 359 | T = isl_mat_identity(ctx: B->ctx, n_row: B->n_col); |
| 360 | else if (B->n_row == 1) |
| 361 | T = parameter_compression_1(B, d); |
| 362 | else |
| 363 | T = parameter_compression_multi(B, d); |
| 364 | T = isl_mat_left_hermite(M: T, neg: 0, NULL, NULL); |
| 365 | if (!T) |
| 366 | goto error; |
| 367 | isl_mat_sub_copy(ctx: T->ctx, dst: T->row + 1, src: cst->row, n_row: cst->n_row, dst_col: 0, src_col: 0, n_col: 1); |
| 368 | isl_mat_free(mat: cst); |
| 369 | isl_mat_free(mat: B); |
| 370 | isl_vec_free(vec: d); |
| 371 | return T; |
| 372 | error2: |
| 373 | isl_int_clear(D); |
| 374 | error: |
| 375 | isl_mat_free(mat: cst); |
| 376 | isl_mat_free(mat: B); |
| 377 | isl_vec_free(vec: d); |
| 378 | return NULL; |
| 379 | } |
| 380 | |
| 381 | /* Given a set of equalities |
| 382 | * |
| 383 | * B(y) + A x = 0 (*) |
| 384 | * |
| 385 | * compute and return an affine transformation T, |
| 386 | * |
| 387 | * y = T y' |
| 388 | * |
| 389 | * that bijectively maps the integer vectors y' to integer |
| 390 | * vectors y that satisfy the modulo constraints for some value of x. |
| 391 | * |
| 392 | * Let [H 0] be the Hermite Normal Form of A, i.e., |
| 393 | * |
| 394 | * A = [H 0] Q |
| 395 | * |
| 396 | * Then y is a solution of (*) iff |
| 397 | * |
| 398 | * H^-1 B(y) (= - [I 0] Q x) |
| 399 | * |
| 400 | * is an integer vector. Let d be the common denominator of H^-1. |
| 401 | * We impose |
| 402 | * |
| 403 | * d H^-1 B(y) = 0 mod d |
| 404 | * |
| 405 | * and compute the solution using isl_mat_parameter_compression. |
| 406 | */ |
| 407 | __isl_give isl_mat *isl_mat_parameter_compression_ext(__isl_take isl_mat *B, |
| 408 | __isl_take isl_mat *A) |
| 409 | { |
| 410 | isl_ctx *ctx; |
| 411 | isl_vec *d; |
| 412 | int n_row, n_col; |
| 413 | |
| 414 | if (!A) |
| 415 | return isl_mat_free(mat: B); |
| 416 | |
| 417 | ctx = isl_mat_get_ctx(mat: A); |
| 418 | n_row = A->n_row; |
| 419 | n_col = A->n_col; |
| 420 | A = isl_mat_left_hermite(M: A, neg: 0, NULL, NULL); |
| 421 | A = isl_mat_drop_cols(mat: A, col: n_row, n: n_col - n_row); |
| 422 | A = isl_mat_lin_to_aff(mat: A); |
| 423 | A = isl_mat_right_inverse(mat: A); |
| 424 | d = isl_vec_alloc(ctx, size: n_row); |
| 425 | if (A) |
| 426 | d = isl_vec_set(vec: d, v: A->row[0][0]); |
| 427 | A = isl_mat_drop_rows(mat: A, row: 0, n: 1); |
| 428 | A = isl_mat_drop_cols(mat: A, col: 0, n: 1); |
| 429 | B = isl_mat_product(left: A, right: B); |
| 430 | |
| 431 | return isl_mat_parameter_compression(B, d); |
| 432 | } |
| 433 | |
| 434 | /* Return a compression matrix that indicates that there are no solutions |
| 435 | * to the original constraints. In particular, return a zero-column |
| 436 | * matrix with 1 + dim rows. If "T2" is not NULL, then assign *T2 |
| 437 | * the inverse of this matrix. *T2 may already have been assigned |
| 438 | * matrix, so free it first. |
| 439 | * "free1", "free2" and "free3" are temporary matrices that are |
| 440 | * not useful when an empty compression is returned. They are |
| 441 | * simply freed. |
| 442 | */ |
| 443 | static __isl_give isl_mat *empty_compression(isl_ctx *ctx, unsigned dim, |
| 444 | __isl_give isl_mat **T2, __isl_take isl_mat *free1, |
| 445 | __isl_take isl_mat *free2, __isl_take isl_mat *free3) |
| 446 | { |
| 447 | isl_mat_free(mat: free1); |
| 448 | isl_mat_free(mat: free2); |
| 449 | isl_mat_free(mat: free3); |
| 450 | if (T2) { |
| 451 | isl_mat_free(mat: *T2); |
| 452 | *T2 = isl_mat_alloc(ctx, n_row: 0, n_col: 1 + dim); |
| 453 | } |
| 454 | return isl_mat_alloc(ctx, n_row: 1 + dim, n_col: 0); |
| 455 | } |
| 456 | |
| 457 | /* Given a matrix that maps a (possibly) parametric domain to |
| 458 | * a parametric domain, add in rows that map the "nparam" parameters onto |
| 459 | * themselves. |
| 460 | */ |
| 461 | static __isl_give isl_mat *insert_parameter_rows(__isl_take isl_mat *mat, |
| 462 | unsigned nparam) |
| 463 | { |
| 464 | int i; |
| 465 | |
| 466 | if (nparam == 0) |
| 467 | return mat; |
| 468 | if (!mat) |
| 469 | return NULL; |
| 470 | |
| 471 | mat = isl_mat_insert_rows(mat, row: 1, n: nparam); |
| 472 | if (!mat) |
| 473 | return NULL; |
| 474 | |
| 475 | for (i = 0; i < nparam; ++i) { |
| 476 | isl_seq_clr(p: mat->row[1 + i], len: mat->n_col); |
| 477 | isl_int_set(mat->row[1 + i][1 + i], mat->row[0][0]); |
| 478 | } |
| 479 | |
| 480 | return mat; |
| 481 | } |
| 482 | |
| 483 | /* Given a set of equalities |
| 484 | * |
| 485 | * -C(y) + M x = 0 |
| 486 | * |
| 487 | * this function computes a unimodular transformation from a lower-dimensional |
| 488 | * space to the original space that bijectively maps the integer points x' |
| 489 | * in the lower-dimensional space to the integer points x in the original |
| 490 | * space that satisfy the equalities. |
| 491 | * |
| 492 | * The input is given as a matrix B = [ -C M ] and the output is a |
| 493 | * matrix that maps [1 x'] to [1 x]. |
| 494 | * The number of equality constraints in B is assumed to be smaller than |
| 495 | * or equal to the number of variables x. |
| 496 | * "first" is the position of the first x variable. |
| 497 | * The preceding variables are considered to be y-variables. |
| 498 | * If T2 is not NULL, then *T2 is set to a matrix mapping [1 x] to [1 x']. |
| 499 | * |
| 500 | * First compute the (left) Hermite normal form of M, |
| 501 | * |
| 502 | * M [U1 U2] = M U = H = [H1 0] |
| 503 | * or |
| 504 | * M = H Q = [H1 0] [Q1] |
| 505 | * [Q2] |
| 506 | * |
| 507 | * with U, Q unimodular, Q = U^{-1} (and H lower triangular). |
| 508 | * Define the transformed variables as |
| 509 | * |
| 510 | * x = [U1 U2] [ x1' ] = [U1 U2] [Q1] x |
| 511 | * [ x2' ] [Q2] |
| 512 | * |
| 513 | * The equalities then become |
| 514 | * |
| 515 | * -C(y) + H1 x1' = 0 or x1' = H1^{-1} C(y) = C'(y) |
| 516 | * |
| 517 | * If the denominator of the constant term does not divide the |
| 518 | * the common denominator of the coefficients of y, then every |
| 519 | * integer point is mapped to a non-integer point and then the original set |
| 520 | * has no integer solutions (since the x' are a unimodular transformation |
| 521 | * of the x). In this case, a zero-column matrix is returned. |
| 522 | * Otherwise, the transformation is given by |
| 523 | * |
| 524 | * x = U1 H1^{-1} C(y) + U2 x2' |
| 525 | * |
| 526 | * The inverse transformation is simply |
| 527 | * |
| 528 | * x2' = Q2 x |
| 529 | */ |
| 530 | __isl_give isl_mat *isl_mat_final_variable_compression(__isl_take isl_mat *B, |
| 531 | int first, __isl_give isl_mat **T2) |
| 532 | { |
| 533 | int i, n; |
| 534 | isl_ctx *ctx; |
| 535 | isl_mat *H = NULL, *C, *H1, *U = NULL, *U1, *U2; |
| 536 | unsigned dim; |
| 537 | |
| 538 | if (T2) |
| 539 | *T2 = NULL; |
| 540 | if (!B) |
| 541 | goto error; |
| 542 | |
| 543 | ctx = isl_mat_get_ctx(mat: B); |
| 544 | dim = B->n_col - 1; |
| 545 | n = dim - first; |
| 546 | if (n < B->n_row) |
| 547 | isl_die(ctx, isl_error_invalid, "too many equality constraints" , |
| 548 | goto error); |
| 549 | H = isl_mat_sub_alloc(mat: B, first_row: 0, n_row: B->n_row, first_col: 1 + first, n_col: n); |
| 550 | H = isl_mat_left_hermite(M: H, neg: 0, U: &U, Q: T2); |
| 551 | if (!H || !U || (T2 && !*T2)) |
| 552 | goto error; |
| 553 | if (T2) { |
| 554 | *T2 = isl_mat_drop_rows(mat: *T2, row: 0, n: B->n_row); |
| 555 | *T2 = isl_mat_diagonal(mat1: isl_mat_identity(ctx, n_row: 1 + first), mat2: *T2); |
| 556 | if (!*T2) |
| 557 | goto error; |
| 558 | } |
| 559 | C = isl_mat_alloc(ctx, n_row: 1 + B->n_row, n_col: 1 + first); |
| 560 | if (!C) |
| 561 | goto error; |
| 562 | isl_int_set_si(C->row[0][0], 1); |
| 563 | isl_seq_clr(p: C->row[0] + 1, len: first); |
| 564 | isl_mat_sub_neg(ctx, dst: C->row + 1, src: B->row, n_row: B->n_row, dst_col: 0, src_col: 0, n_col: 1 + first); |
| 565 | H1 = isl_mat_sub_alloc(mat: H, first_row: 0, n_row: H->n_row, first_col: 0, n_col: H->n_row); |
| 566 | H1 = isl_mat_lin_to_aff(mat: H1); |
| 567 | C = isl_mat_inverse_product(left: H1, right: C); |
| 568 | if (!C) |
| 569 | goto error; |
| 570 | isl_mat_free(mat: H); |
| 571 | if (!isl_int_is_one(C->row[0][0])) { |
| 572 | isl_int g; |
| 573 | |
| 574 | isl_int_init(g); |
| 575 | for (i = 0; i < B->n_row; ++i) { |
| 576 | isl_seq_gcd(p: C->row[1 + i] + 1, len: first, gcd: &g); |
| 577 | isl_int_gcd(g, g, C->row[0][0]); |
| 578 | if (!isl_int_is_divisible_by(C->row[1 + i][0], g)) |
| 579 | break; |
| 580 | } |
| 581 | isl_int_clear(g); |
| 582 | |
| 583 | if (i < B->n_row) |
| 584 | return empty_compression(ctx, dim, T2, free1: B, free2: C, free3: U); |
| 585 | C = isl_mat_normalize(mat: C); |
| 586 | } |
| 587 | U1 = isl_mat_sub_alloc(mat: U, first_row: 0, n_row: U->n_row, first_col: 0, n_col: B->n_row); |
| 588 | U1 = isl_mat_lin_to_aff(mat: U1); |
| 589 | U2 = isl_mat_sub_alloc(mat: U, first_row: 0, n_row: U->n_row, first_col: B->n_row, n_col: U->n_row - B->n_row); |
| 590 | U2 = isl_mat_lin_to_aff(mat: U2); |
| 591 | isl_mat_free(mat: U); |
| 592 | C = isl_mat_product(left: U1, right: C); |
| 593 | C = isl_mat_aff_direct_sum(left: C, right: U2); |
| 594 | C = insert_parameter_rows(mat: C, nparam: first); |
| 595 | |
| 596 | isl_mat_free(mat: B); |
| 597 | |
| 598 | return C; |
| 599 | error: |
| 600 | isl_mat_free(mat: B); |
| 601 | isl_mat_free(mat: H); |
| 602 | isl_mat_free(mat: U); |
| 603 | if (T2) { |
| 604 | isl_mat_free(mat: *T2); |
| 605 | *T2 = NULL; |
| 606 | } |
| 607 | return NULL; |
| 608 | } |
| 609 | |
| 610 | /* Given a set of equalities |
| 611 | * |
| 612 | * M x - c = 0 |
| 613 | * |
| 614 | * this function computes a unimodular transformation from a lower-dimensional |
| 615 | * space to the original space that bijectively maps the integer points x' |
| 616 | * in the lower-dimensional space to the integer points x in the original |
| 617 | * space that satisfy the equalities. |
| 618 | * |
| 619 | * The input is given as a matrix B = [ -c M ] and the output is a |
| 620 | * matrix that maps [1 x'] to [1 x]. |
| 621 | * The number of equality constraints in B is assumed to be smaller than |
| 622 | * or equal to the number of variables x. |
| 623 | * If T2 is not NULL, then *T2 is set to a matrix mapping [1 x] to [1 x']. |
| 624 | */ |
| 625 | __isl_give isl_mat *isl_mat_variable_compression(__isl_take isl_mat *B, |
| 626 | __isl_give isl_mat **T2) |
| 627 | { |
| 628 | return isl_mat_final_variable_compression(B, first: 0, T2); |
| 629 | } |
| 630 | |
| 631 | /* Return "bset" and set *T and *T2 to the identity transformation |
| 632 | * on "bset" (provided T and T2 are not NULL). |
| 633 | */ |
| 634 | static __isl_give isl_basic_set *return_with_identity( |
| 635 | __isl_take isl_basic_set *bset, __isl_give isl_mat **T, |
| 636 | __isl_give isl_mat **T2) |
| 637 | { |
| 638 | isl_size dim; |
| 639 | isl_mat *id; |
| 640 | |
| 641 | dim = isl_basic_set_dim(bset, type: isl_dim_set); |
| 642 | if (dim < 0) |
| 643 | return isl_basic_set_free(bset); |
| 644 | if (!T && !T2) |
| 645 | return bset; |
| 646 | |
| 647 | id = isl_mat_identity(ctx: isl_basic_map_get_ctx(bmap: bset), n_row: 1 + dim); |
| 648 | if (T) |
| 649 | *T = isl_mat_copy(mat: id); |
| 650 | if (T2) |
| 651 | *T2 = isl_mat_copy(mat: id); |
| 652 | isl_mat_free(mat: id); |
| 653 | |
| 654 | return bset; |
| 655 | } |
| 656 | |
| 657 | /* Use the n equalities of bset to unimodularly transform the |
| 658 | * variables x such that n transformed variables x1' have a constant value |
| 659 | * and rewrite the constraints of bset in terms of the remaining |
| 660 | * transformed variables x2'. The matrix pointed to by T maps |
| 661 | * the new variables x2' back to the original variables x, while T2 |
| 662 | * maps the original variables to the new variables. |
| 663 | */ |
| 664 | static __isl_give isl_basic_set *compress_variables( |
| 665 | __isl_take isl_basic_set *bset, |
| 666 | __isl_give isl_mat **T, __isl_give isl_mat **T2) |
| 667 | { |
| 668 | struct isl_mat *B, *TC; |
| 669 | isl_size dim; |
| 670 | |
| 671 | if (T) |
| 672 | *T = NULL; |
| 673 | if (T2) |
| 674 | *T2 = NULL; |
| 675 | if (isl_basic_set_check_no_params(bset) < 0 || |
| 676 | isl_basic_set_check_no_locals(bset) < 0) |
| 677 | return isl_basic_set_free(bset); |
| 678 | dim = isl_basic_set_dim(bset, type: isl_dim_set); |
| 679 | if (dim < 0) |
| 680 | return isl_basic_set_free(bset); |
| 681 | isl_assert(bset->ctx, bset->n_eq <= dim, goto error); |
| 682 | if (bset->n_eq == 0) |
| 683 | return return_with_identity(bset, T, T2); |
| 684 | |
| 685 | B = isl_mat_sub_alloc6(ctx: bset->ctx, row: bset->eq, first_row: 0, n_row: bset->n_eq, first_col: 0, n_col: 1 + dim); |
| 686 | TC = isl_mat_variable_compression(B, T2); |
| 687 | if (!TC) |
| 688 | goto error; |
| 689 | if (TC->n_col == 0) { |
| 690 | isl_mat_free(mat: TC); |
| 691 | if (T2) { |
| 692 | isl_mat_free(mat: *T2); |
| 693 | *T2 = NULL; |
| 694 | } |
| 695 | bset = isl_basic_set_set_to_empty(bset); |
| 696 | return return_with_identity(bset, T, T2); |
| 697 | } |
| 698 | |
| 699 | bset = isl_basic_set_preimage(bset, mat: T ? isl_mat_copy(mat: TC) : TC); |
| 700 | if (T) |
| 701 | *T = TC; |
| 702 | return bset; |
| 703 | error: |
| 704 | isl_basic_set_free(bset); |
| 705 | return NULL; |
| 706 | } |
| 707 | |
| 708 | __isl_give isl_basic_set *isl_basic_set_remove_equalities( |
| 709 | __isl_take isl_basic_set *bset, __isl_give isl_mat **T, |
| 710 | __isl_give isl_mat **T2) |
| 711 | { |
| 712 | if (T) |
| 713 | *T = NULL; |
| 714 | if (T2) |
| 715 | *T2 = NULL; |
| 716 | if (isl_basic_set_check_no_params(bset) < 0) |
| 717 | return isl_basic_set_free(bset); |
| 718 | bset = isl_basic_set_gauss(bset, NULL); |
| 719 | if (ISL_F_ISSET(bset, ISL_BASIC_SET_EMPTY)) |
| 720 | return return_with_identity(bset, T, T2); |
| 721 | bset = compress_variables(bset, T, T2); |
| 722 | return bset; |
| 723 | } |
| 724 | |
| 725 | /* Check if dimension dim belongs to a residue class |
| 726 | * i_dim \equiv r mod m |
| 727 | * with m != 1 and if so return m in *modulo and r in *residue. |
| 728 | * As a special case, when i_dim has a fixed value v, then |
| 729 | * *modulo is set to 0 and *residue to v. |
| 730 | * |
| 731 | * If i_dim does not belong to such a residue class, then *modulo |
| 732 | * is set to 1 and *residue is set to 0. |
| 733 | */ |
| 734 | isl_stat isl_basic_set_dim_residue_class(__isl_keep isl_basic_set *bset, |
| 735 | int pos, isl_int *modulo, isl_int *residue) |
| 736 | { |
| 737 | isl_bool fixed; |
| 738 | struct isl_ctx *ctx; |
| 739 | struct isl_mat *H = NULL, *U = NULL, *C, *H1, *U1; |
| 740 | isl_size total; |
| 741 | isl_size nparam; |
| 742 | |
| 743 | if (!bset || !modulo || !residue) |
| 744 | return isl_stat_error; |
| 745 | |
| 746 | fixed = isl_basic_set_plain_dim_is_fixed(bset, dim: pos, val: residue); |
| 747 | if (fixed < 0) |
| 748 | return isl_stat_error; |
| 749 | if (fixed) { |
| 750 | isl_int_set_si(*modulo, 0); |
| 751 | return isl_stat_ok; |
| 752 | } |
| 753 | |
| 754 | ctx = isl_basic_set_get_ctx(bset); |
| 755 | total = isl_basic_set_dim(bset, type: isl_dim_all); |
| 756 | nparam = isl_basic_set_dim(bset, type: isl_dim_param); |
| 757 | if (total < 0 || nparam < 0) |
| 758 | return isl_stat_error; |
| 759 | H = isl_mat_sub_alloc6(ctx, row: bset->eq, first_row: 0, n_row: bset->n_eq, first_col: 1, n_col: total); |
| 760 | H = isl_mat_left_hermite(M: H, neg: 0, U: &U, NULL); |
| 761 | if (!H) |
| 762 | return isl_stat_error; |
| 763 | |
| 764 | isl_seq_gcd(p: U->row[nparam + pos]+bset->n_eq, |
| 765 | len: total-bset->n_eq, gcd: modulo); |
| 766 | if (isl_int_is_zero(*modulo)) |
| 767 | isl_int_set_si(*modulo, 1); |
| 768 | if (isl_int_is_one(*modulo)) { |
| 769 | isl_int_set_si(*residue, 0); |
| 770 | isl_mat_free(mat: H); |
| 771 | isl_mat_free(mat: U); |
| 772 | return isl_stat_ok; |
| 773 | } |
| 774 | |
| 775 | C = isl_mat_alloc(ctx, n_row: 1 + bset->n_eq, n_col: 1); |
| 776 | if (!C) |
| 777 | goto error; |
| 778 | isl_int_set_si(C->row[0][0], 1); |
| 779 | isl_mat_sub_neg(ctx, dst: C->row + 1, src: bset->eq, n_row: bset->n_eq, dst_col: 0, src_col: 0, n_col: 1); |
| 780 | H1 = isl_mat_sub_alloc(mat: H, first_row: 0, n_row: H->n_row, first_col: 0, n_col: H->n_row); |
| 781 | H1 = isl_mat_lin_to_aff(mat: H1); |
| 782 | C = isl_mat_inverse_product(left: H1, right: C); |
| 783 | isl_mat_free(mat: H); |
| 784 | U1 = isl_mat_sub_alloc(mat: U, first_row: nparam+pos, n_row: 1, first_col: 0, n_col: bset->n_eq); |
| 785 | U1 = isl_mat_lin_to_aff(mat: U1); |
| 786 | isl_mat_free(mat: U); |
| 787 | C = isl_mat_product(left: U1, right: C); |
| 788 | if (!C) |
| 789 | return isl_stat_error; |
| 790 | if (!isl_int_is_divisible_by(C->row[1][0], C->row[0][0])) { |
| 791 | bset = isl_basic_set_copy(bset); |
| 792 | bset = isl_basic_set_set_to_empty(bset); |
| 793 | isl_basic_set_free(bset); |
| 794 | isl_int_set_si(*modulo, 1); |
| 795 | isl_int_set_si(*residue, 0); |
| 796 | return isl_stat_ok; |
| 797 | } |
| 798 | isl_int_divexact(*residue, C->row[1][0], C->row[0][0]); |
| 799 | isl_int_fdiv_r(*residue, *residue, *modulo); |
| 800 | isl_mat_free(mat: C); |
| 801 | return isl_stat_ok; |
| 802 | error: |
| 803 | isl_mat_free(mat: H); |
| 804 | isl_mat_free(mat: U); |
| 805 | return isl_stat_error; |
| 806 | } |
| 807 | |
| 808 | /* Check if dimension dim belongs to a residue class |
| 809 | * i_dim \equiv r mod m |
| 810 | * with m != 1 and if so return m in *modulo and r in *residue. |
| 811 | * As a special case, when i_dim has a fixed value v, then |
| 812 | * *modulo is set to 0 and *residue to v. |
| 813 | * |
| 814 | * If i_dim does not belong to such a residue class, then *modulo |
| 815 | * is set to 1 and *residue is set to 0. |
| 816 | */ |
| 817 | isl_stat isl_set_dim_residue_class(__isl_keep isl_set *set, |
| 818 | int pos, isl_int *modulo, isl_int *residue) |
| 819 | { |
| 820 | isl_int m; |
| 821 | isl_int r; |
| 822 | int i; |
| 823 | |
| 824 | if (!set || !modulo || !residue) |
| 825 | return isl_stat_error; |
| 826 | |
| 827 | if (set->n == 0) { |
| 828 | isl_int_set_si(*modulo, 0); |
| 829 | isl_int_set_si(*residue, 0); |
| 830 | return isl_stat_ok; |
| 831 | } |
| 832 | |
| 833 | if (isl_basic_set_dim_residue_class(bset: set->p[0], pos, modulo, residue)<0) |
| 834 | return isl_stat_error; |
| 835 | |
| 836 | if (set->n == 1) |
| 837 | return isl_stat_ok; |
| 838 | |
| 839 | if (isl_int_is_one(*modulo)) |
| 840 | return isl_stat_ok; |
| 841 | |
| 842 | isl_int_init(m); |
| 843 | isl_int_init(r); |
| 844 | |
| 845 | for (i = 1; i < set->n; ++i) { |
| 846 | if (isl_basic_set_dim_residue_class(bset: set->p[i], pos, modulo: &m, residue: &r) < 0) |
| 847 | goto error; |
| 848 | isl_int_gcd(*modulo, *modulo, m); |
| 849 | isl_int_sub(m, *residue, r); |
| 850 | isl_int_gcd(*modulo, *modulo, m); |
| 851 | if (!isl_int_is_zero(*modulo)) |
| 852 | isl_int_fdiv_r(*residue, *residue, *modulo); |
| 853 | if (isl_int_is_one(*modulo)) |
| 854 | break; |
| 855 | } |
| 856 | |
| 857 | isl_int_clear(m); |
| 858 | isl_int_clear(r); |
| 859 | |
| 860 | return isl_stat_ok; |
| 861 | error: |
| 862 | isl_int_clear(m); |
| 863 | isl_int_clear(r); |
| 864 | return isl_stat_error; |
| 865 | } |
| 866 | |
| 867 | /* Check if dimension "dim" belongs to a residue class |
| 868 | * i_dim \equiv r mod m |
| 869 | * with m != 1 and if so return m in *modulo and r in *residue. |
| 870 | * As a special case, when i_dim has a fixed value v, then |
| 871 | * *modulo is set to 0 and *residue to v. |
| 872 | * |
| 873 | * If i_dim does not belong to such a residue class, then *modulo |
| 874 | * is set to 1 and *residue is set to 0. |
| 875 | */ |
| 876 | isl_stat isl_set_dim_residue_class_val(__isl_keep isl_set *set, |
| 877 | int pos, __isl_give isl_val **modulo, __isl_give isl_val **residue) |
| 878 | { |
| 879 | *modulo = NULL; |
| 880 | *residue = NULL; |
| 881 | if (!set) |
| 882 | return isl_stat_error; |
| 883 | *modulo = isl_val_alloc(ctx: isl_set_get_ctx(set)); |
| 884 | *residue = isl_val_alloc(ctx: isl_set_get_ctx(set)); |
| 885 | if (!*modulo || !*residue) |
| 886 | goto error; |
| 887 | if (isl_set_dim_residue_class(set, pos, |
| 888 | modulo: &(*modulo)->n, residue: &(*residue)->n) < 0) |
| 889 | goto error; |
| 890 | isl_int_set_si((*modulo)->d, 1); |
| 891 | isl_int_set_si((*residue)->d, 1); |
| 892 | return isl_stat_ok; |
| 893 | error: |
| 894 | isl_val_free(v: *modulo); |
| 895 | isl_val_free(v: *residue); |
| 896 | return isl_stat_error; |
| 897 | } |
| 898 | |