| 1 | /* |
| 2 | * Copyright 2010-2011 INRIA Saclay |
| 3 | * Copyright 2014 Ecole Normale Superieure |
| 4 | * |
| 5 | * Use of this software is governed by the MIT license |
| 6 | * |
| 7 | * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, |
| 8 | * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, |
| 9 | * 91893 Orsay, France |
| 10 | * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France |
| 11 | */ |
| 12 | |
| 13 | #include <isl_map_private.h> |
| 14 | #include <isl_aff_private.h> |
| 15 | #include <isl_morph.h> |
| 16 | #include <isl_seq.h> |
| 17 | #include <isl_mat_private.h> |
| 18 | #include <isl_space_private.h> |
| 19 | #include <isl_equalities.h> |
| 20 | #include <isl_id_private.h> |
| 21 | #include <isl_aff_private.h> |
| 22 | #include <isl_vec_private.h> |
| 23 | |
| 24 | isl_ctx *isl_morph_get_ctx(__isl_keep isl_morph *morph) |
| 25 | { |
| 26 | if (!morph) |
| 27 | return NULL; |
| 28 | return isl_basic_set_get_ctx(bset: morph->dom); |
| 29 | } |
| 30 | |
| 31 | __isl_give isl_morph *isl_morph_alloc( |
| 32 | __isl_take isl_basic_set *dom, __isl_take isl_basic_set *ran, |
| 33 | __isl_take isl_mat *map, __isl_take isl_mat *inv) |
| 34 | { |
| 35 | isl_morph *morph; |
| 36 | |
| 37 | if (!dom || !ran || !map || !inv) |
| 38 | goto error; |
| 39 | |
| 40 | morph = isl_alloc_type(dom->ctx, struct isl_morph); |
| 41 | if (!morph) |
| 42 | goto error; |
| 43 | |
| 44 | morph->ref = 1; |
| 45 | morph->dom = dom; |
| 46 | morph->ran = ran; |
| 47 | morph->map = map; |
| 48 | morph->inv = inv; |
| 49 | |
| 50 | return morph; |
| 51 | error: |
| 52 | isl_basic_set_free(bset: dom); |
| 53 | isl_basic_set_free(bset: ran); |
| 54 | isl_mat_free(mat: map); |
| 55 | isl_mat_free(mat: inv); |
| 56 | return NULL; |
| 57 | } |
| 58 | |
| 59 | __isl_give isl_morph *isl_morph_copy(__isl_keep isl_morph *morph) |
| 60 | { |
| 61 | if (!morph) |
| 62 | return NULL; |
| 63 | |
| 64 | morph->ref++; |
| 65 | return morph; |
| 66 | } |
| 67 | |
| 68 | __isl_give isl_morph *isl_morph_dup(__isl_keep isl_morph *morph) |
| 69 | { |
| 70 | if (!morph) |
| 71 | return NULL; |
| 72 | |
| 73 | return isl_morph_alloc(dom: isl_basic_set_copy(bset: morph->dom), |
| 74 | ran: isl_basic_set_copy(bset: morph->ran), |
| 75 | map: isl_mat_copy(mat: morph->map), inv: isl_mat_copy(mat: morph->inv)); |
| 76 | } |
| 77 | |
| 78 | __isl_give isl_morph *isl_morph_cow(__isl_take isl_morph *morph) |
| 79 | { |
| 80 | if (!morph) |
| 81 | return NULL; |
| 82 | |
| 83 | if (morph->ref == 1) |
| 84 | return morph; |
| 85 | morph->ref--; |
| 86 | return isl_morph_dup(morph); |
| 87 | } |
| 88 | |
| 89 | __isl_null isl_morph *isl_morph_free(__isl_take isl_morph *morph) |
| 90 | { |
| 91 | if (!morph) |
| 92 | return NULL; |
| 93 | |
| 94 | if (--morph->ref > 0) |
| 95 | return NULL; |
| 96 | |
| 97 | isl_basic_set_free(bset: morph->dom); |
| 98 | isl_basic_set_free(bset: morph->ran); |
| 99 | isl_mat_free(mat: morph->map); |
| 100 | isl_mat_free(mat: morph->inv); |
| 101 | free(ptr: morph); |
| 102 | |
| 103 | return NULL; |
| 104 | } |
| 105 | |
| 106 | /* Is "morph" an identity on the parameters? |
| 107 | */ |
| 108 | static isl_bool identity_on_parameters(__isl_keep isl_morph *morph) |
| 109 | { |
| 110 | isl_bool is_identity; |
| 111 | isl_size nparam, nparam_ran; |
| 112 | isl_mat *sub; |
| 113 | |
| 114 | nparam = isl_morph_dom_dim(morph, type: isl_dim_param); |
| 115 | nparam_ran = isl_morph_ran_dim(morph, type: isl_dim_param); |
| 116 | if (nparam < 0 || nparam_ran < 0) |
| 117 | return isl_bool_error; |
| 118 | if (nparam != nparam_ran) |
| 119 | return isl_bool_false; |
| 120 | if (nparam == 0) |
| 121 | return isl_bool_true; |
| 122 | sub = isl_mat_sub_alloc(mat: morph->map, first_row: 0, n_row: 1 + nparam, first_col: 0, n_col: 1 + nparam); |
| 123 | is_identity = isl_mat_is_scaled_identity(mat: sub); |
| 124 | isl_mat_free(mat: sub); |
| 125 | |
| 126 | return is_identity; |
| 127 | } |
| 128 | |
| 129 | /* Return an affine expression of the variables of the range of "morph" |
| 130 | * in terms of the parameters and the variables of the domain on "morph". |
| 131 | * |
| 132 | * In order for the space manipulations to make sense, we require |
| 133 | * that the parameters are not modified by "morph". |
| 134 | */ |
| 135 | __isl_give isl_multi_aff *isl_morph_get_var_multi_aff( |
| 136 | __isl_keep isl_morph *morph) |
| 137 | { |
| 138 | isl_space *dom, *ran, *space; |
| 139 | isl_local_space *ls; |
| 140 | isl_multi_aff *ma; |
| 141 | isl_size nparam, nvar; |
| 142 | int i; |
| 143 | isl_bool is_identity; |
| 144 | |
| 145 | if (!morph) |
| 146 | return NULL; |
| 147 | |
| 148 | is_identity = identity_on_parameters(morph); |
| 149 | if (is_identity < 0) |
| 150 | return NULL; |
| 151 | if (!is_identity) |
| 152 | isl_die(isl_morph_get_ctx(morph), isl_error_invalid, |
| 153 | "cannot handle parameter compression" , return NULL); |
| 154 | |
| 155 | dom = isl_morph_get_dom_space(morph); |
| 156 | ls = isl_local_space_from_space(space: isl_space_copy(space: dom)); |
| 157 | ran = isl_morph_get_ran_space(morph); |
| 158 | space = isl_space_map_from_domain_and_range(domain: dom, range: ran); |
| 159 | ma = isl_multi_aff_zero(space); |
| 160 | |
| 161 | nparam = isl_multi_aff_dim(multi: ma, type: isl_dim_param); |
| 162 | nvar = isl_multi_aff_dim(multi: ma, type: isl_dim_out); |
| 163 | if (nparam < 0 || nvar < 0) |
| 164 | ma = isl_multi_aff_free(multi: ma); |
| 165 | for (i = 0; i < nvar; ++i) { |
| 166 | isl_val *val; |
| 167 | isl_vec *v; |
| 168 | isl_aff *aff; |
| 169 | |
| 170 | v = isl_mat_get_row(mat: morph->map, row: 1 + nparam + i); |
| 171 | v = isl_vec_insert_els(vec: v, pos: 0, n: 1); |
| 172 | val = isl_mat_get_element_val(mat: morph->map, row: 0, col: 0); |
| 173 | v = isl_vec_set_element_val(vec: v, pos: 0, v: val); |
| 174 | aff = isl_aff_alloc_vec(ls: isl_local_space_copy(ls), v); |
| 175 | ma = isl_multi_aff_set_aff(multi: ma, pos: i, el: aff); |
| 176 | } |
| 177 | |
| 178 | isl_local_space_free(ls); |
| 179 | return ma; |
| 180 | } |
| 181 | |
| 182 | /* Return the domain space of "morph". |
| 183 | */ |
| 184 | static __isl_keep isl_space *isl_morph_peek_dom_space( |
| 185 | __isl_keep isl_morph *morph) |
| 186 | { |
| 187 | if (!morph) |
| 188 | return NULL; |
| 189 | |
| 190 | return isl_basic_set_peek_space(bset: morph->dom); |
| 191 | } |
| 192 | |
| 193 | /* Return a copy of the domain space of "morph". |
| 194 | */ |
| 195 | __isl_give isl_space *isl_morph_get_dom_space(__isl_keep isl_morph *morph) |
| 196 | { |
| 197 | return isl_space_copy(space: isl_morph_peek_dom_space(morph)); |
| 198 | } |
| 199 | |
| 200 | /* Check that the match against "space" with result "match" was successful. |
| 201 | */ |
| 202 | static isl_stat check_space_match(__isl_keep isl_space *space, isl_bool match) |
| 203 | { |
| 204 | if (match < 0) |
| 205 | return isl_stat_error; |
| 206 | if (!match) |
| 207 | isl_die(isl_space_get_ctx(space), isl_error_invalid, |
| 208 | "spaces don't match" , return isl_stat_error); |
| 209 | |
| 210 | return isl_stat_ok; |
| 211 | } |
| 212 | |
| 213 | /* Check that "morph" can be applied to the "space". |
| 214 | */ |
| 215 | isl_stat isl_morph_check_applies(__isl_keep isl_morph *morph, |
| 216 | __isl_keep isl_space *space) |
| 217 | { |
| 218 | isl_space *dom_space; |
| 219 | isl_bool applies; |
| 220 | |
| 221 | dom_space = isl_morph_peek_dom_space(morph); |
| 222 | applies = isl_space_is_equal(space1: dom_space, space2: space); |
| 223 | return check_space_match(space, match: applies); |
| 224 | } |
| 225 | |
| 226 | __isl_give isl_space *isl_morph_get_ran_space(__isl_keep isl_morph *morph) |
| 227 | { |
| 228 | if (!morph) |
| 229 | return NULL; |
| 230 | |
| 231 | return isl_space_copy(space: morph->ran->dim); |
| 232 | } |
| 233 | |
| 234 | isl_size isl_morph_dom_dim(__isl_keep isl_morph *morph, enum isl_dim_type type) |
| 235 | { |
| 236 | if (!morph) |
| 237 | return isl_size_error; |
| 238 | |
| 239 | return isl_basic_set_dim(bset: morph->dom, type); |
| 240 | } |
| 241 | |
| 242 | isl_size isl_morph_ran_dim(__isl_keep isl_morph *morph, enum isl_dim_type type) |
| 243 | { |
| 244 | if (!morph) |
| 245 | return isl_size_error; |
| 246 | |
| 247 | return isl_basic_set_dim(bset: morph->ran, type); |
| 248 | } |
| 249 | |
| 250 | __isl_give isl_morph *isl_morph_remove_dom_dims(__isl_take isl_morph *morph, |
| 251 | enum isl_dim_type type, unsigned first, unsigned n) |
| 252 | { |
| 253 | unsigned dom_offset; |
| 254 | |
| 255 | if (n == 0) |
| 256 | return morph; |
| 257 | |
| 258 | morph = isl_morph_cow(morph); |
| 259 | if (!morph) |
| 260 | return NULL; |
| 261 | |
| 262 | dom_offset = 1 + isl_space_offset(space: morph->dom->dim, type); |
| 263 | |
| 264 | morph->dom = isl_basic_set_remove_dims(bset: morph->dom, type, first, n); |
| 265 | |
| 266 | morph->map = isl_mat_drop_cols(mat: morph->map, col: dom_offset + first, n); |
| 267 | |
| 268 | morph->inv = isl_mat_drop_rows(mat: morph->inv, row: dom_offset + first, n); |
| 269 | |
| 270 | if (morph->dom && morph->ran && morph->map && morph->inv) |
| 271 | return morph; |
| 272 | |
| 273 | isl_morph_free(morph); |
| 274 | return NULL; |
| 275 | } |
| 276 | |
| 277 | __isl_give isl_morph *isl_morph_remove_ran_dims(__isl_take isl_morph *morph, |
| 278 | enum isl_dim_type type, unsigned first, unsigned n) |
| 279 | { |
| 280 | unsigned ran_offset; |
| 281 | |
| 282 | if (n == 0) |
| 283 | return morph; |
| 284 | |
| 285 | morph = isl_morph_cow(morph); |
| 286 | if (!morph) |
| 287 | return NULL; |
| 288 | |
| 289 | ran_offset = 1 + isl_space_offset(space: morph->ran->dim, type); |
| 290 | |
| 291 | morph->ran = isl_basic_set_remove_dims(bset: morph->ran, type, first, n); |
| 292 | |
| 293 | morph->map = isl_mat_drop_rows(mat: morph->map, row: ran_offset + first, n); |
| 294 | |
| 295 | morph->inv = isl_mat_drop_cols(mat: morph->inv, col: ran_offset + first, n); |
| 296 | |
| 297 | if (morph->dom && morph->ran && morph->map && morph->inv) |
| 298 | return morph; |
| 299 | |
| 300 | isl_morph_free(morph); |
| 301 | return NULL; |
| 302 | } |
| 303 | |
| 304 | /* Project domain of morph onto its parameter domain. |
| 305 | */ |
| 306 | __isl_give isl_morph *isl_morph_dom_params(__isl_take isl_morph *morph) |
| 307 | { |
| 308 | isl_size n; |
| 309 | |
| 310 | morph = isl_morph_cow(morph); |
| 311 | if (!morph) |
| 312 | return NULL; |
| 313 | n = isl_basic_set_dim(bset: morph->dom, type: isl_dim_set); |
| 314 | if (n < 0) |
| 315 | return isl_morph_free(morph); |
| 316 | morph = isl_morph_remove_dom_dims(morph, type: isl_dim_set, first: 0, n); |
| 317 | if (!morph) |
| 318 | return NULL; |
| 319 | morph->dom = isl_basic_set_params(bset: morph->dom); |
| 320 | if (morph->dom) |
| 321 | return morph; |
| 322 | |
| 323 | isl_morph_free(morph); |
| 324 | return NULL; |
| 325 | } |
| 326 | |
| 327 | /* Project range of morph onto its parameter domain. |
| 328 | */ |
| 329 | __isl_give isl_morph *isl_morph_ran_params(__isl_take isl_morph *morph) |
| 330 | { |
| 331 | isl_size n; |
| 332 | |
| 333 | morph = isl_morph_cow(morph); |
| 334 | if (!morph) |
| 335 | return NULL; |
| 336 | n = isl_basic_set_dim(bset: morph->ran, type: isl_dim_set); |
| 337 | if (n < 0) |
| 338 | return isl_morph_free(morph); |
| 339 | morph = isl_morph_remove_ran_dims(morph, type: isl_dim_set, first: 0, n); |
| 340 | if (!morph) |
| 341 | return NULL; |
| 342 | morph->ran = isl_basic_set_params(bset: morph->ran); |
| 343 | if (morph->ran) |
| 344 | return morph; |
| 345 | |
| 346 | isl_morph_free(morph); |
| 347 | return NULL; |
| 348 | } |
| 349 | |
| 350 | /* Replace the identifier of the tuple of the range of the morph by "id". |
| 351 | */ |
| 352 | static __isl_give isl_morph *isl_morph_set_ran_tuple_id( |
| 353 | __isl_take isl_morph *morph, __isl_keep isl_id *id) |
| 354 | { |
| 355 | morph = isl_morph_cow(morph); |
| 356 | if (!morph) |
| 357 | return NULL; |
| 358 | morph->ran = isl_basic_set_set_tuple_id(bset: morph->ran, id: isl_id_copy(id)); |
| 359 | if (!morph->ran) |
| 360 | return isl_morph_free(morph); |
| 361 | return morph; |
| 362 | } |
| 363 | |
| 364 | void isl_morph_print_internal(__isl_take isl_morph *morph, FILE *out) |
| 365 | { |
| 366 | if (!morph) |
| 367 | return; |
| 368 | |
| 369 | isl_basic_set_dump(bset: morph->dom); |
| 370 | isl_basic_set_dump(bset: morph->ran); |
| 371 | isl_mat_print_internal(mat: morph->map, out, indent: 4); |
| 372 | isl_mat_print_internal(mat: morph->inv, out, indent: 4); |
| 373 | } |
| 374 | |
| 375 | void isl_morph_dump(__isl_take isl_morph *morph) |
| 376 | { |
| 377 | isl_morph_print_internal(morph, stderr); |
| 378 | } |
| 379 | |
| 380 | __isl_give isl_morph *isl_morph_identity(__isl_keep isl_basic_set *bset) |
| 381 | { |
| 382 | isl_mat *id; |
| 383 | isl_basic_set *universe; |
| 384 | isl_size total; |
| 385 | |
| 386 | total = isl_basic_set_dim(bset, type: isl_dim_all); |
| 387 | if (total < 0) |
| 388 | return NULL; |
| 389 | |
| 390 | id = isl_mat_identity(ctx: bset->ctx, n_row: 1 + total); |
| 391 | universe = isl_basic_set_universe(space: isl_space_copy(space: bset->dim)); |
| 392 | |
| 393 | return isl_morph_alloc(dom: universe, ran: isl_basic_set_copy(bset: universe), |
| 394 | map: id, inv: isl_mat_copy(mat: id)); |
| 395 | } |
| 396 | |
| 397 | /* Create a(n identity) morphism between empty sets of the same dimension |
| 398 | * a "bset". |
| 399 | */ |
| 400 | __isl_give isl_morph *isl_morph_empty(__isl_keep isl_basic_set *bset) |
| 401 | { |
| 402 | isl_mat *id; |
| 403 | isl_basic_set *empty; |
| 404 | isl_size total; |
| 405 | |
| 406 | total = isl_basic_set_dim(bset, type: isl_dim_all); |
| 407 | if (total < 0) |
| 408 | return NULL; |
| 409 | |
| 410 | id = isl_mat_identity(ctx: bset->ctx, n_row: 1 + total); |
| 411 | empty = isl_basic_set_empty(space: isl_space_copy(space: bset->dim)); |
| 412 | |
| 413 | return isl_morph_alloc(dom: empty, ran: isl_basic_set_copy(bset: empty), |
| 414 | map: id, inv: isl_mat_copy(mat: id)); |
| 415 | } |
| 416 | |
| 417 | /* Construct a basic set described by the "n" equalities of "bset" starting |
| 418 | * at "first". |
| 419 | */ |
| 420 | static __isl_give isl_basic_set *copy_equalities(__isl_keep isl_basic_set *bset, |
| 421 | unsigned first, unsigned n) |
| 422 | { |
| 423 | int i, k; |
| 424 | isl_basic_set *eq; |
| 425 | isl_size total; |
| 426 | |
| 427 | total = isl_basic_set_dim(bset, type: isl_dim_all); |
| 428 | if (total < 0 || isl_basic_set_check_no_locals(bset) < 0) |
| 429 | return NULL; |
| 430 | |
| 431 | eq = isl_basic_set_alloc_space(space: isl_basic_set_get_space(bset), extra: 0, n_eq: n, n_ineq: 0); |
| 432 | if (!eq) |
| 433 | return NULL; |
| 434 | for (i = 0; i < n; ++i) { |
| 435 | k = isl_basic_set_alloc_equality(bset: eq); |
| 436 | if (k < 0) |
| 437 | goto error; |
| 438 | isl_seq_cpy(dst: eq->eq[k], src: bset->eq[first + i], len: 1 + total); |
| 439 | } |
| 440 | |
| 441 | return eq; |
| 442 | error: |
| 443 | isl_basic_set_free(bset: eq); |
| 444 | return NULL; |
| 445 | } |
| 446 | |
| 447 | /* Given a basic set, exploit the equalities in the basic set to construct |
| 448 | * a morphism that maps the basic set to a lower-dimensional space. |
| 449 | * Specifically, the morphism reduces the number of dimensions of type "type". |
| 450 | * |
| 451 | * We first select the equalities of interest, that is those that involve |
| 452 | * variables of type "type" and no later variables. |
| 453 | * Denote those equalities as |
| 454 | * |
| 455 | * -C(p) + M x = 0 |
| 456 | * |
| 457 | * where C(p) depends on the parameters if type == isl_dim_set and |
| 458 | * is a constant if type == isl_dim_param. |
| 459 | * |
| 460 | * Use isl_mat_final_variable_compression to construct a compression |
| 461 | * |
| 462 | * x = T x' |
| 463 | * |
| 464 | * x' = Q x |
| 465 | * |
| 466 | * If T is a zero-column matrix, then the set of equality constraints |
| 467 | * do not admit a solution. In this case, an empty morphism is returned. |
| 468 | * |
| 469 | * Both matrices are extended to map the full original space to the full |
| 470 | * compressed space. |
| 471 | */ |
| 472 | __isl_give isl_morph *isl_basic_set_variable_compression( |
| 473 | __isl_keep isl_basic_set *bset, enum isl_dim_type type) |
| 474 | { |
| 475 | unsigned otype; |
| 476 | isl_size ntype; |
| 477 | unsigned orest; |
| 478 | unsigned nrest; |
| 479 | isl_size total; |
| 480 | int f_eq, n_eq; |
| 481 | isl_space *space; |
| 482 | isl_mat *E, *Q, *C; |
| 483 | isl_basic_set *dom, *ran; |
| 484 | |
| 485 | if (!bset) |
| 486 | return NULL; |
| 487 | |
| 488 | if (isl_basic_set_plain_is_empty(bset)) |
| 489 | return isl_morph_empty(bset); |
| 490 | |
| 491 | if (isl_basic_set_check_no_locals(bset) < 0) |
| 492 | return NULL; |
| 493 | |
| 494 | ntype = isl_basic_set_dim(bset, type); |
| 495 | total = isl_basic_set_dim(bset, type: isl_dim_all); |
| 496 | if (ntype < 0 || total < 0) |
| 497 | return NULL; |
| 498 | otype = isl_basic_set_offset(bset, type); |
| 499 | orest = otype + ntype; |
| 500 | nrest = total - (orest - 1); |
| 501 | |
| 502 | for (f_eq = 0; f_eq < bset->n_eq; ++f_eq) |
| 503 | if (isl_seq_first_non_zero(p: bset->eq[f_eq] + orest, len: nrest) == -1) |
| 504 | break; |
| 505 | for (n_eq = 0; f_eq + n_eq < bset->n_eq; ++n_eq) |
| 506 | if (isl_seq_first_non_zero(p: bset->eq[f_eq + n_eq] + otype, len: ntype) == -1) |
| 507 | break; |
| 508 | if (n_eq == 0) |
| 509 | return isl_morph_identity(bset); |
| 510 | |
| 511 | E = isl_mat_sub_alloc6(ctx: bset->ctx, row: bset->eq, first_row: f_eq, n_row: n_eq, first_col: 0, n_col: orest); |
| 512 | C = isl_mat_final_variable_compression(B: E, first: otype - 1, T2: &Q); |
| 513 | if (!Q) |
| 514 | C = isl_mat_free(mat: C); |
| 515 | if (C && C->n_col == 0) { |
| 516 | isl_mat_free(mat: C); |
| 517 | isl_mat_free(mat: Q); |
| 518 | return isl_morph_empty(bset); |
| 519 | } |
| 520 | |
| 521 | Q = isl_mat_diagonal(mat1: Q, mat2: isl_mat_identity(ctx: bset->ctx, n_row: nrest)); |
| 522 | C = isl_mat_diagonal(mat1: C, mat2: isl_mat_identity(ctx: bset->ctx, n_row: nrest)); |
| 523 | |
| 524 | space = isl_space_copy(space: bset->dim); |
| 525 | space = isl_space_drop_dims(space, type, first: 0, num: ntype); |
| 526 | space = isl_space_add_dims(space, type, n: ntype - n_eq); |
| 527 | ran = isl_basic_set_universe(space); |
| 528 | dom = copy_equalities(bset, first: f_eq, n: n_eq); |
| 529 | |
| 530 | return isl_morph_alloc(dom, ran, map: Q, inv: C); |
| 531 | } |
| 532 | |
| 533 | /* Given a basic set, exploit the equalities in the basic set to construct |
| 534 | * a morphism that maps the basic set to a lower-dimensional space |
| 535 | * with identifier "id". |
| 536 | * Specifically, the morphism reduces the number of set dimensions. |
| 537 | */ |
| 538 | __isl_give isl_morph *isl_basic_set_variable_compression_with_id( |
| 539 | __isl_keep isl_basic_set *bset, __isl_keep isl_id *id) |
| 540 | { |
| 541 | isl_morph *morph; |
| 542 | |
| 543 | morph = isl_basic_set_variable_compression(bset, type: isl_dim_set); |
| 544 | morph = isl_morph_set_ran_tuple_id(morph, id); |
| 545 | return morph; |
| 546 | } |
| 547 | |
| 548 | /* Construct a parameter compression for "bset". |
| 549 | * We basically just call isl_mat_parameter_compression with the right input |
| 550 | * and then extend the resulting matrix to include the variables. |
| 551 | * |
| 552 | * The implementation assumes that "bset" does not have any equalities |
| 553 | * that only involve the parameters and that isl_basic_set_gauss has |
| 554 | * been applied to "bset". |
| 555 | * |
| 556 | * Let the equalities be given as |
| 557 | * |
| 558 | * B(p) + A x = 0. |
| 559 | * |
| 560 | * We use isl_mat_parameter_compression_ext to compute the compression |
| 561 | * |
| 562 | * p = T p'. |
| 563 | */ |
| 564 | __isl_give isl_morph *isl_basic_set_parameter_compression( |
| 565 | __isl_keep isl_basic_set *bset) |
| 566 | { |
| 567 | isl_size nparam; |
| 568 | isl_size nvar; |
| 569 | isl_size n_div; |
| 570 | int n_eq; |
| 571 | isl_mat *H, *B; |
| 572 | isl_mat *map, *inv; |
| 573 | isl_basic_set *dom, *ran; |
| 574 | |
| 575 | if (!bset) |
| 576 | return NULL; |
| 577 | |
| 578 | if (isl_basic_set_plain_is_empty(bset)) |
| 579 | return isl_morph_empty(bset); |
| 580 | if (bset->n_eq == 0) |
| 581 | return isl_morph_identity(bset); |
| 582 | |
| 583 | n_eq = bset->n_eq; |
| 584 | nparam = isl_basic_set_dim(bset, type: isl_dim_param); |
| 585 | nvar = isl_basic_set_dim(bset, type: isl_dim_set); |
| 586 | n_div = isl_basic_set_dim(bset, type: isl_dim_div); |
| 587 | if (nparam < 0 || nvar < 0 || n_div < 0) |
| 588 | return NULL; |
| 589 | |
| 590 | if (isl_seq_first_non_zero(p: bset->eq[bset->n_eq - 1] + 1 + nparam, |
| 591 | len: nvar + n_div) == -1) |
| 592 | isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid, |
| 593 | "input not allowed to have parameter equalities" , |
| 594 | return NULL); |
| 595 | if (n_eq > nvar + n_div) |
| 596 | isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid, |
| 597 | "input not gaussed" , return NULL); |
| 598 | |
| 599 | B = isl_mat_sub_alloc6(ctx: bset->ctx, row: bset->eq, first_row: 0, n_row: n_eq, first_col: 0, n_col: 1 + nparam); |
| 600 | H = isl_mat_sub_alloc6(ctx: bset->ctx, row: bset->eq, |
| 601 | first_row: 0, n_row: n_eq, first_col: 1 + nparam, n_col: nvar + n_div); |
| 602 | inv = isl_mat_parameter_compression_ext(B, A: H); |
| 603 | inv = isl_mat_diagonal(mat1: inv, mat2: isl_mat_identity(ctx: bset->ctx, n_row: nvar)); |
| 604 | map = isl_mat_right_inverse(mat: isl_mat_copy(mat: inv)); |
| 605 | |
| 606 | dom = isl_basic_set_universe(space: isl_space_copy(space: bset->dim)); |
| 607 | ran = isl_basic_set_universe(space: isl_space_copy(space: bset->dim)); |
| 608 | |
| 609 | return isl_morph_alloc(dom, ran, map, inv); |
| 610 | } |
| 611 | |
| 612 | /* Construct an isl_multi_aff that corresponds |
| 613 | * to the affine transformation matrix "mat" and |
| 614 | * that lives in an anonymous space. |
| 615 | */ |
| 616 | static __isl_give isl_multi_aff *isl_multi_aff_from_aff_mat_anonymous( |
| 617 | __isl_take isl_mat *mat) |
| 618 | { |
| 619 | isl_size n_row, n_col; |
| 620 | isl_ctx *ctx; |
| 621 | isl_space *space; |
| 622 | |
| 623 | ctx = isl_mat_get_ctx(mat); |
| 624 | n_row = isl_mat_rows(mat); |
| 625 | n_col = isl_mat_cols(mat); |
| 626 | if (n_row < 0 || n_col < 0) |
| 627 | space = NULL; |
| 628 | else |
| 629 | space = isl_space_alloc(ctx, nparam: 0, n_in: n_col - 1, n_out: n_row - 1); |
| 630 | |
| 631 | return isl_multi_aff_from_aff_mat(space, mat); |
| 632 | } |
| 633 | |
| 634 | /* Apply the morphism to the basic set. |
| 635 | * In particular, compute the preimage of "bset" under the inverse mapping |
| 636 | * in morph and intersect with the range of the morphism. |
| 637 | * Note that the mapping in morph applies to both parameters and set dimensions, |
| 638 | * so the parameters need to be treated as set dimensions during the call |
| 639 | * to isl_basic_set_preimage_multi_aff. |
| 640 | */ |
| 641 | __isl_give isl_basic_set *isl_morph_basic_set(__isl_take isl_morph *morph, |
| 642 | __isl_take isl_basic_set *bset) |
| 643 | { |
| 644 | isl_size n_param; |
| 645 | isl_space *space; |
| 646 | isl_multi_aff *ma; |
| 647 | |
| 648 | if (!morph || isl_basic_set_check_equal_space(bset1: bset, bset2: morph->dom) < 0) |
| 649 | goto error; |
| 650 | n_param = isl_basic_set_dim(bset: morph->dom, type: isl_dim_param); |
| 651 | if (n_param < 0) |
| 652 | goto error; |
| 653 | |
| 654 | ma = isl_multi_aff_from_aff_mat_anonymous(mat: isl_mat_copy(mat: morph->inv)); |
| 655 | |
| 656 | bset = isl_basic_set_move_dims(bset, dst_type: isl_dim_set, dst_pos: 0, |
| 657 | src_type: isl_dim_param, src_pos: 0, n: n_param); |
| 658 | bset = isl_basic_set_preimage_multi_aff(bset, ma); |
| 659 | space = isl_basic_set_get_space(bset: morph->ran); |
| 660 | bset = isl_basic_set_reset_space(bset, space); |
| 661 | bset = isl_basic_set_intersect(bset1: bset, bset2: isl_basic_set_copy(bset: morph->ran)); |
| 662 | |
| 663 | isl_morph_free(morph); |
| 664 | return bset; |
| 665 | error: |
| 666 | isl_morph_free(morph); |
| 667 | isl_basic_set_free(bset); |
| 668 | return NULL; |
| 669 | } |
| 670 | |
| 671 | /* Apply the morphism to the set. |
| 672 | * In particular, compute the preimage of "set" under the inverse mapping |
| 673 | * in morph and intersect with the range of the morphism. |
| 674 | * Note that the mapping in morph applies to both parameters and set dimensions, |
| 675 | * so the parameters need to be treated as set dimensions during the call |
| 676 | * to isl_set_preimage_multi_aff. |
| 677 | */ |
| 678 | __isl_give isl_set *isl_morph_set(__isl_take isl_morph *morph, |
| 679 | __isl_take isl_set *set) |
| 680 | { |
| 681 | isl_size n_param; |
| 682 | isl_space *space; |
| 683 | isl_multi_aff *ma; |
| 684 | isl_basic_set *ran; |
| 685 | |
| 686 | if (!morph || isl_set_basic_set_check_equal_space(set, bset: morph->dom) < 0) |
| 687 | goto error; |
| 688 | n_param = isl_basic_set_dim(bset: morph->dom, type: isl_dim_param); |
| 689 | if (n_param < 0) |
| 690 | goto error; |
| 691 | |
| 692 | ma = isl_multi_aff_from_aff_mat_anonymous(mat: isl_mat_copy(mat: morph->inv)); |
| 693 | |
| 694 | set = isl_set_move_dims(set, dst_type: isl_dim_set, dst_pos: 0, src_type: isl_dim_param, src_pos: 0, n: n_param); |
| 695 | set = isl_set_preimage_multi_aff(set, ma); |
| 696 | space = isl_basic_set_get_space(bset: morph->ran); |
| 697 | set = isl_set_reset_space(set, space); |
| 698 | ran = isl_basic_set_copy(bset: morph->ran); |
| 699 | set = isl_set_intersect(set1: set, set2: isl_set_from_basic_set(bset: ran)); |
| 700 | |
| 701 | isl_morph_free(morph); |
| 702 | return set; |
| 703 | error: |
| 704 | isl_set_free(set); |
| 705 | isl_morph_free(morph); |
| 706 | return NULL; |
| 707 | } |
| 708 | |
| 709 | /* Construct a morphism that first does morph2 and then morph1. |
| 710 | */ |
| 711 | __isl_give isl_morph *isl_morph_compose(__isl_take isl_morph *morph1, |
| 712 | __isl_take isl_morph *morph2) |
| 713 | { |
| 714 | isl_mat *map, *inv; |
| 715 | isl_basic_set *dom, *ran; |
| 716 | |
| 717 | if (!morph1 || !morph2) |
| 718 | goto error; |
| 719 | |
| 720 | map = isl_mat_product(left: isl_mat_copy(mat: morph1->map), right: isl_mat_copy(mat: morph2->map)); |
| 721 | inv = isl_mat_product(left: isl_mat_copy(mat: morph2->inv), right: isl_mat_copy(mat: morph1->inv)); |
| 722 | dom = isl_morph_basic_set(morph: isl_morph_inverse(morph: isl_morph_copy(morph: morph2)), |
| 723 | bset: isl_basic_set_copy(bset: morph1->dom)); |
| 724 | dom = isl_basic_set_intersect(bset1: dom, bset2: isl_basic_set_copy(bset: morph2->dom)); |
| 725 | ran = isl_morph_basic_set(morph: isl_morph_copy(morph: morph1), |
| 726 | bset: isl_basic_set_copy(bset: morph2->ran)); |
| 727 | ran = isl_basic_set_intersect(bset1: ran, bset2: isl_basic_set_copy(bset: morph1->ran)); |
| 728 | |
| 729 | isl_morph_free(morph: morph1); |
| 730 | isl_morph_free(morph: morph2); |
| 731 | |
| 732 | return isl_morph_alloc(dom, ran, map, inv); |
| 733 | error: |
| 734 | isl_morph_free(morph: morph1); |
| 735 | isl_morph_free(morph: morph2); |
| 736 | return NULL; |
| 737 | } |
| 738 | |
| 739 | __isl_give isl_morph *isl_morph_inverse(__isl_take isl_morph *morph) |
| 740 | { |
| 741 | isl_basic_set *bset; |
| 742 | isl_mat *mat; |
| 743 | |
| 744 | morph = isl_morph_cow(morph); |
| 745 | if (!morph) |
| 746 | return NULL; |
| 747 | |
| 748 | bset = morph->dom; |
| 749 | morph->dom = morph->ran; |
| 750 | morph->ran = bset; |
| 751 | |
| 752 | mat = morph->map; |
| 753 | morph->map = morph->inv; |
| 754 | morph->inv = mat; |
| 755 | |
| 756 | return morph; |
| 757 | } |
| 758 | |
| 759 | /* We detect all the equalities first to avoid implicit equalities |
| 760 | * being discovered during the computations. In particular, |
| 761 | * the compression on the variables could expose additional stride |
| 762 | * constraints on the parameters. This would result in existentially |
| 763 | * quantified variables after applying the resulting morph, which |
| 764 | * in turn could break invariants of the calling functions. |
| 765 | */ |
| 766 | __isl_give isl_morph *isl_basic_set_full_compression( |
| 767 | __isl_keep isl_basic_set *bset) |
| 768 | { |
| 769 | isl_morph *morph, *morph2; |
| 770 | |
| 771 | bset = isl_basic_set_copy(bset); |
| 772 | bset = isl_basic_set_detect_equalities(bset); |
| 773 | |
| 774 | morph = isl_basic_set_variable_compression(bset, type: isl_dim_param); |
| 775 | bset = isl_morph_basic_set(morph: isl_morph_copy(morph), bset); |
| 776 | |
| 777 | morph2 = isl_basic_set_parameter_compression(bset); |
| 778 | bset = isl_morph_basic_set(morph: isl_morph_copy(morph: morph2), bset); |
| 779 | |
| 780 | morph = isl_morph_compose(morph1: morph2, morph2: morph); |
| 781 | |
| 782 | morph2 = isl_basic_set_variable_compression(bset, type: isl_dim_set); |
| 783 | isl_basic_set_free(bset); |
| 784 | |
| 785 | morph = isl_morph_compose(morph1: morph2, morph2: morph); |
| 786 | |
| 787 | return morph; |
| 788 | } |
| 789 | |
| 790 | __isl_give isl_vec *isl_morph_vec(__isl_take isl_morph *morph, |
| 791 | __isl_take isl_vec *vec) |
| 792 | { |
| 793 | if (!morph) |
| 794 | goto error; |
| 795 | |
| 796 | vec = isl_mat_vec_product(mat: isl_mat_copy(mat: morph->map), vec); |
| 797 | |
| 798 | isl_morph_free(morph); |
| 799 | return vec; |
| 800 | error: |
| 801 | isl_morph_free(morph); |
| 802 | isl_vec_free(vec); |
| 803 | return NULL; |
| 804 | } |
| 805 | |