3b32e19457afd2fde5de226d39be8444995c6310
[platform/upstream/isl.git] / isl_factorization.c
1 /*
2  * Copyright 2005-2007 Universiteit Leiden
3  * Copyright 2008-2009 Katholieke Universiteit Leuven
4  * Copyright 2010      INRIA Saclay
5  *
6  * Use of this software is governed by the GNU LGPLv2.1 license
7  *
8  * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science,
9  * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands
10  * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A,
11  * B-3001 Leuven, Belgium
12  * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
13  * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France 
14  */
15
16 #include <isl_factorization.h>
17 #include <isl_dim_private.h>
18 #include <isl_mat_private.h>
19
20 static __isl_give isl_factorizer *isl_factorizer_alloc(
21         __isl_take isl_morph *morph, int n_group)
22 {
23         isl_factorizer *f = NULL;
24         int *len = NULL;
25
26         if (!morph)
27                 return NULL;
28
29         if (n_group > 0) {
30                 len = isl_alloc_array(morph->dom->ctx, int, n_group);
31                 if (!len)
32                         goto error;
33         }
34
35         f = isl_alloc_type(morph->dom->ctx, struct isl_factorizer);
36         if (!f)
37                 goto error;
38
39         f->morph = morph;
40         f->n_group = n_group;
41         f->len = len;
42
43         return f;
44 error:
45         free(len);
46         isl_morph_free(morph);
47         return NULL;
48 }
49
50 void isl_factorizer_free(__isl_take isl_factorizer *f)
51 {
52         if (!f)
53                 return;
54
55         isl_morph_free(f->morph);
56         free(f->len);
57         free(f);
58 }
59
60 void isl_factorizer_dump(__isl_take isl_factorizer *f, FILE *out)
61 {
62         int i;
63
64         if (!f)
65                 return;
66
67         isl_morph_dump(f->morph, out);
68         fprintf(out, "[");
69         for (i = 0; i < f->n_group; ++i) {
70                 if (i)
71                         fprintf(out, ", ");
72                 fprintf(out, "%d", f->len[i]);
73         }
74         fprintf(out, "]\n");
75 }
76
77 __isl_give isl_factorizer *isl_factorizer_identity(__isl_keep isl_basic_set *bset)
78 {
79         return isl_factorizer_alloc(isl_morph_identity(bset), 0);
80 }
81
82 __isl_give isl_factorizer *isl_factorizer_groups(__isl_keep isl_basic_set *bset,
83         __isl_take isl_mat *Q, __isl_take isl_mat *U, int n, int *len)
84 {
85         int i;
86         unsigned nvar;
87         unsigned ovar;
88         isl_dim *dim;
89         isl_basic_set *dom;
90         isl_basic_set *ran;
91         isl_morph *morph;
92         isl_factorizer *f;
93         isl_mat *id;
94
95         if (!bset || !Q || !U)
96                 goto error;
97
98         ovar = 1 + isl_dim_offset(bset->dim, isl_dim_set);
99         id = isl_mat_identity(bset->ctx, ovar);
100         Q = isl_mat_diagonal(isl_mat_copy(id), Q);
101         U = isl_mat_diagonal(id, U);
102
103         nvar = isl_basic_set_dim(bset, isl_dim_set);
104         dim = isl_basic_set_get_dim(bset);
105         dom = isl_basic_set_universe(isl_dim_copy(dim));
106         dim = isl_dim_drop(dim, isl_dim_set, 0, nvar);
107         dim = isl_dim_add(dim, isl_dim_set, nvar);
108         ran = isl_basic_set_universe(dim);
109         morph = isl_morph_alloc(dom, ran, Q, U);
110         f = isl_factorizer_alloc(morph, n);
111         if (!f)
112                 return NULL;
113         for (i = 0; i < n; ++i)
114                 f->len[i] = len[i];
115         return f;
116 error:
117         isl_mat_free(Q);
118         isl_mat_free(U);
119         return NULL;
120 }
121
122 struct isl_factor_groups {
123         int *pos;               /* for each column: row position of pivot */
124         int *group;             /* group to which a column belongs */
125         int *cnt;               /* number of columns in the group */
126         int *rowgroup;          /* group to which a constraint belongs */
127 };
128
129 /* Initialize isl_factor_groups structure: find pivot row positions,
130  * each column initially belongs to its own group and the groups
131  * of the constraints are still unknown.
132  */
133 static int init_groups(struct isl_factor_groups *g, __isl_keep isl_mat *H)
134 {
135         int i, j;
136
137         if (!H)
138                 return -1;
139
140         g->pos = isl_alloc_array(H->ctx, int, H->n_col);
141         g->group = isl_alloc_array(H->ctx, int, H->n_col);
142         g->cnt = isl_alloc_array(H->ctx, int, H->n_col);
143         g->rowgroup = isl_alloc_array(H->ctx, int, H->n_row);
144
145         if (!g->pos || !g->group || !g->cnt || !g->rowgroup)
146                 return -1;
147
148         for (i = 0; i < H->n_row; ++i)
149                 g->rowgroup[i] = -1;
150         for (i = 0, j = 0; i < H->n_col; ++i) {
151                 for ( ; j < H->n_row; ++j)
152                         if (!isl_int_is_zero(H->row[j][i]))
153                                 break;
154                 g->pos[i] = j;
155         }
156         for (i = 0; i < H->n_col; ++i) {
157                 g->group[i] = i;
158                 g->cnt[i] = 1;
159         }
160
161         return 0;
162 }
163
164 /* Update group[k] to the group column k belongs to.
165  * When merging two groups, only the group of the current
166  * group leader is changed.  Here we change the group of
167  * the other members to also point to the group that the
168  * old group leader now points to.
169  */
170 static void update_group(struct isl_factor_groups *g, int k)
171 {
172         int p = g->group[k];
173         while (g->cnt[p] == 0)
174                 p = g->group[p];
175         g->group[k] = p;
176 }
177
178 /* Merge group i with all groups of the subsequent columns
179  * with non-zero coefficients in row j of H.
180  * (The previous columns are all zero; otherwise we would have handled
181  * the row before.)
182  */
183 static int update_group_i_with_row_j(struct isl_factor_groups *g, int i, int j,
184         __isl_keep isl_mat *H)
185 {
186         int k;
187
188         g->rowgroup[j] = g->group[i];
189         for (k = i + 1; k < H->n_col && j >= g->pos[k]; ++k) {
190                 update_group(g, k);
191                 update_group(g, i);
192                 if (g->group[k] != g->group[i] &&
193                     !isl_int_is_zero(H->row[j][k])) {
194                         isl_assert(H->ctx, g->cnt[g->group[k]] != 0, return -1);
195                         isl_assert(H->ctx, g->cnt[g->group[i]] != 0, return -1);
196                         if (g->group[i] < g->group[k]) {
197                                 g->cnt[g->group[i]] += g->cnt[g->group[k]];
198                                 g->cnt[g->group[k]] = 0;
199                                 g->group[g->group[k]] = g->group[i];
200                         } else {
201                                 g->cnt[g->group[k]] += g->cnt[g->group[i]];
202                                 g->cnt[g->group[i]] = 0;
203                                 g->group[g->group[i]] = g->group[k];
204                         }
205                 }
206         }
207
208         return 0;
209 }
210
211 /* Update the group information based on the constraint matrix.
212  */
213 static int update_groups(struct isl_factor_groups *g, __isl_keep isl_mat *H)
214 {
215         int i, j;
216
217         for (i = 0; i < H->n_col && g->cnt[0] < H->n_col; ++i) {
218                 if (g->pos[i] == H->n_row)
219                         continue; /* A line direction */
220                 if (g->rowgroup[g->pos[i]] == -1)
221                         g->rowgroup[g->pos[i]] = i;
222                 for (j = g->pos[i] + 1; j < H->n_row; ++j) {
223                         if (isl_int_is_zero(H->row[j][i]))
224                                 continue;
225                         if (g->rowgroup[j] != -1)
226                                 continue;
227                         if (update_group_i_with_row_j(g, i, j, H) < 0)
228                                 return -1;
229                 }
230         }
231
232         return 0;
233 }
234
235 static void clear_groups(struct isl_factor_groups *g)
236 {
237         if (!g)
238                 return;
239         free(g->pos);
240         free(g->group);
241         free(g->cnt);
242         free(g->rowgroup);
243 }
244
245 /* Determine if the set variables of the basic set can be factorized and
246  * return the results in an isl_factorizer.
247  *
248  * The algorithm works by first computing the Hermite normal form
249  * and then grouping columns linked by one or more constraints together,
250  * where a constraints "links" two or more columns if the constraint
251  * has nonzero coefficients in the columns.
252  */
253 __isl_give isl_factorizer *isl_basic_set_factorizer(
254         __isl_keep isl_basic_set *bset)
255 {
256         int i, j, n, done;
257         isl_mat *H, *U, *Q;
258         unsigned nvar;
259         struct isl_factor_groups g = { 0 };
260         isl_factorizer *f;
261
262         if (!bset)
263                 return NULL;
264
265         isl_assert(bset->ctx, isl_basic_set_dim(bset, isl_dim_div) == 0,
266                 return NULL);
267
268         nvar = isl_basic_set_dim(bset, isl_dim_set);
269         if (nvar <= 1)
270                 return isl_factorizer_identity(bset);
271
272         H = isl_mat_alloc(bset->ctx, bset->n_eq + bset->n_ineq, nvar);
273         if (!H)
274                 return NULL;
275         isl_mat_sub_copy(bset->ctx, H->row, bset->eq, bset->n_eq,
276                 0, 1 + isl_dim_offset(bset->dim, isl_dim_set), nvar);
277         isl_mat_sub_copy(bset->ctx, H->row + bset->n_eq, bset->ineq, bset->n_ineq,
278                 0, 1 + isl_dim_offset(bset->dim, isl_dim_set), nvar);
279         H = isl_mat_left_hermite(H, 0, &U, &Q);
280
281         if (init_groups(&g, H) < 0)
282                 goto error;
283         if (update_groups(&g, H) < 0)
284                 goto error;
285
286         if (g.cnt[0] == nvar) {
287                 isl_mat_free(H);
288                 isl_mat_free(U);
289                 isl_mat_free(Q);
290                 clear_groups(&g);
291
292                 return isl_factorizer_identity(bset);
293         }
294
295         done = 0;
296         n = 0;
297         while (done != nvar) {
298                 int group = g.group[done];
299                 for (i = 1; i < g.cnt[group]; ++i) {
300                         if (g.group[done + i] == group)
301                                 continue;
302                         for (j = done + g.cnt[group]; j < nvar; ++j)
303                                 if (g.group[j] == group)
304                                         break;
305                         g.group[j] = g.group[done + i];
306                         Q = isl_mat_swap_rows(Q, done + i, j);
307                         U = isl_mat_swap_cols(U, done + i, j);
308                 }
309                 done += g.cnt[group];
310                 g.pos[n++] = g.cnt[group];
311         }
312
313         f = isl_factorizer_groups(bset, Q, U, n, g.pos);
314
315         isl_mat_free(H);
316         clear_groups(&g);
317
318         return f;
319 error:
320         isl_mat_free(H);
321         isl_mat_free(U);
322         isl_mat_free(Q);
323         clear_groups(&g);
324         return NULL;
325 }