export neg_left_hermite
[barvinok.git] / topcom.c
blob47bea41f2c2dc1c1caffac1ae3a430f5eccee5e2
1 #include <barvinok/util.h>
2 #include <barvinok/options.h>
3 #include <unistd.h>
4 #include "topcom.h"
5 #include "config.h"
7 #define ALLOC(type) (type*)malloc(sizeof(type))
9 void run_points2triangs(pid_t *child, int *in, int *out)
11 int in_fd[2], out_fd[2];
13 if (pipe(in_fd))
14 assert(0);
15 if (pipe(out_fd))
16 assert(0);
17 *child = fork();
18 assert(*child >= 0);
19 if (!*child) {
20 int rc;
22 dup2(in_fd[0], 0);
23 dup2(out_fd[1], 1);
24 close(in_fd[0]);
25 close(out_fd[1]);
26 close(in_fd[1]);
27 close(out_fd[0]);
29 rc = execl(POINTS2TRIANGS_PATH, "points2triangs", "--regular", NULL);
30 assert(0);
32 close(in_fd[0]);
33 close(out_fd[1]);
34 *in = in_fd[1];
35 *out = out_fd[0];
38 struct domain {
39 Param_Domain domain;
40 int F_len;
43 static Param_Vertices *construct_vertex(unsigned *vertex_facets,
44 Matrix *Constraints,
45 int d, unsigned nparam, unsigned MaxRays)
47 unsigned nvar = Constraints->NbColumns-2 - nparam;
48 Matrix *A = Matrix_Alloc(nvar+1, nvar+1);
49 Matrix *inv = Matrix_Alloc(nvar+1, nvar+1);
50 Matrix *B = Matrix_Alloc(nvar, nparam+2);
51 Matrix *V = Matrix_Alloc(nvar, nparam+2);
52 Matrix *Domain = Matrix_Alloc(d-nvar, nparam+2);
53 Polyhedron *AD;
54 unsigned bx;
55 int i, j, ix;
56 int ok;
57 Param_Vertices *vertex;
59 for (j = 0, i = 0, ix = 0, bx = MSB; i < d; ++i) {
60 if ((vertex_facets[ix] & bx) == bx) {
61 Vector_Copy(Constraints->p[i]+1, A->p[j], nvar);
62 Vector_Oppose(Constraints->p[i]+1+nvar, B->p[j++], nparam+1);
64 NEXT(ix, bx);
66 assert(j == nvar);
67 value_set_si(A->p[nvar][nvar], 1);
68 ok = Matrix_Inverse(A, inv);
69 assert(ok);
70 Matrix_Free(A);
71 inv->NbRows = nvar;
72 inv->NbColumns = nvar;
73 Matrix_Product(inv, B, V);
74 Matrix_Free(B);
75 for (i = 0; i < nvar; ++i) {
76 value_assign(V->p[i][nparam+1], inv->p[nvar][nvar]);
77 Vector_Normalize(V->p[i], V->NbColumns);
79 Matrix_Free(inv);
80 for (j = 0, i = 0, ix = 0, bx = MSB; i < d; ++i) {
81 if ((vertex_facets[ix] & bx) == bx) {
82 NEXT(ix, bx);
83 continue;
85 Param_Inner_Product(Constraints->p[i], V, Domain->p[j]);
86 if (First_Non_Zero(Domain->p[j]+1, nparam+1) == -1)
87 vertex_facets[ix] |= bx;
88 else
89 value_set_si(Domain->p[j++][0], 1);
90 NEXT(ix, bx);
92 Domain->NbRows = j;
93 A = Matrix_Copy(Domain);
94 AD = Constraints2Polyhedron(A, MaxRays);
95 Matrix_Free(A);
96 POL_ENSURE_VERTICES(AD);
97 /* A vertex with a lower-dimensional activity domain
98 * saturates more facets than derived above and is actually
99 * the superimposition of two or more vertices.
100 * We therefore discard the domain and (ultimately)
101 * the chamber containing it.
102 * We keep the vertex, though, since it may reappear
103 * in other chambers, which will then likewise be discarded.
104 * The same holds if the activity domain is empty.
106 if (AD->NbEq > 0) {
107 Matrix_Free(Domain);
108 Domain = NULL;
110 Polyhedron_Free(AD);
111 vertex = calloc(1, sizeof(Param_Vertices));
112 vertex->Facets = vertex_facets;
113 vertex->Vertex = V;
114 vertex->Domain = Domain;
115 return vertex;
118 static int add_vertex_to_domain(Param_Vertices **vertices, int words,
119 unsigned *vertex_facets,
120 Matrix *Constraints, int d, unsigned nparam,
121 struct domain *domain,
122 unsigned MaxRays)
124 Param_Vertices *vertex;
125 unsigned vbx;
126 int vi, vix;
128 for (vi = 0, vix = 0, vbx = MSB;
129 *vertices;
130 vertices = &(*vertices)->next, ++vi) {
131 int i;
132 for (i = 0; i < words; ++i)
133 if (((*vertices)->Facets[i] & vertex_facets[i]) != vertex_facets[i])
134 break;
135 if (i >= words) {
136 if (!(*vertices)->Domain)
137 domain->F_len = 0;
138 else
139 domain->domain.F[vix] |= vbx;
140 free(vertex_facets);
141 return 0;
143 NEXT(vix, vbx);
145 if (domain->F_len <= vix) {
146 domain->F_len++;
147 domain->domain.F = realloc(domain->domain.F,
148 domain->F_len * sizeof(unsigned));
149 domain->domain.F[domain->F_len-1] = 0;
151 vertex = construct_vertex(vertex_facets, Constraints, d, nparam, MaxRays);
152 if (!vertex->Domain)
153 domain->F_len = 0;
154 else
155 domain->domain.F[vix] |= vbx;
156 vertex->next = *vertices;
157 *vertices = vertex;
158 return 1;
161 static int bit_count(unsigned *F, int F_len)
163 int i;
164 int count = 0;
166 for (i = 0; i < F_len; ++i) {
167 unsigned v = F[i];
168 while (v) {
169 v &= v-1;
170 ++count;
173 return count;
176 static void compute_domain(struct domain *domain, Param_Vertices *vertices,
177 Polyhedron *C, unsigned MaxRays)
179 unsigned bx;
180 int i, ix, j;
181 int nbV = bit_count(domain->domain.F, domain->F_len);
182 unsigned cols = vertices->Domain->NbColumns;
183 unsigned rows = vertices->Domain->NbRows;
184 Matrix *Constraints = Matrix_Alloc(nbV * rows + C->NbConstraints, cols);
186 for (i = 0, j = 0, ix = 0, bx = MSB;
187 vertices;
188 vertices = vertices->next, ++i) {
189 if ((domain->domain.F[ix] & bx) == bx)
190 Vector_Copy(vertices->Domain->p[0],
191 Constraints->p[(j++)*rows], rows * cols);
192 NEXT(ix, bx);
194 Vector_Copy(C->Constraint[0], Constraints->p[j*rows], C->NbConstraints * cols);
195 domain->domain.Domain = Constraints2Polyhedron(Constraints, MaxRays);
196 Matrix_Free(Constraints);
199 static void add_domain(struct domain **domains, struct domain *domain,
200 Param_Vertices *vertices, Polyhedron *C,
201 struct barvinok_options *options)
203 options->stats->topcom_chambers++;
205 for (; *domains; domains = (struct domain **)&(*domains)->domain.next) {
206 int i;
207 for (i = 0; i < (*domains)->F_len; ++i)
208 if (((*domains)->domain.F[i] & domain->domain.F[i])
209 != domain->domain.F[i])
210 break;
211 if (i < (*domains)->F_len)
212 continue;
213 for (; i < domain->F_len; ++i)
214 if (domain->domain.F[i])
215 break;
216 if (i >= domain->F_len) {
217 Param_Domain_Free(&domain->domain);
218 return;
221 options->stats->topcom_distinct_chambers++;
222 compute_domain(domain, vertices, C, options->MaxRays);
223 *domains = domain;
226 #define INT_BITS (sizeof(unsigned) * 8)
228 /* Remove any empty or lower-dimensional chamber. The latter
229 * lie on the boundary of the context and are facets of other chambers.
231 * While we are examining each chamber, also extend the F vector
232 * of each chamber to the maximum.
234 static void remove_empty_chambers(Param_Domain **PD, unsigned vertex_words)
236 while (*PD) {
237 int remove = 0;
238 int i;
240 if ((*PD)->Domain->NbEq > 0)
241 remove = 1;
242 else {
243 POL_ENSURE_FACETS((*PD)->Domain);
244 if ((*PD)->Domain->NbEq > 0)
245 remove = 1;
247 if (remove) {
248 Param_Domain *D = *PD;
249 *PD = (*PD)->next;
250 D->next = NULL;
251 Param_Domain_Free(D);
252 continue;
254 if ((i = ((struct domain*)(*PD))->F_len) < vertex_words)
255 (*PD)->F = realloc((*PD)->F, vertex_words * sizeof(unsigned));
256 for (; i < vertex_words; ++i)
257 (*PD)->F[i] = 0;
258 PD = &(*PD)->next;
262 static Param_Polyhedron *points2triangs(Matrix *K, Polyhedron *P, Polyhedron *C,
263 struct barvinok_options *options)
265 int in, out;
266 int i, j;
267 pid_t child;
268 FILE *fin, *fout;
269 int d = K->NbRows;
270 int words = (d+INT_BITS-1)/INT_BITS;
271 Param_Vertices *vertices = NULL;
272 struct domain *domains = NULL;
273 int vertex_words = 1;
274 Param_Polyhedron *PP = ALLOC(Param_Polyhedron);
275 unsigned MaxRays = options->MaxRays;
277 PP->nbV = 0;
278 PP->Constraints = Polyhedron2Constraints(P);
279 /* We need the exact facets, because we may make some of them open later */
280 POL_UNSET(options->MaxRays, POL_INTEGER);
282 run_points2triangs(&child, &in, &out);
284 fin = fdopen(in, "w");
285 fprintf(fin, "[\n");
286 for (i = 0; i < K->NbRows; ++i) {
287 fprintf(fin, "[");
288 for (j = 0; j < K->NbColumns; ++j)
289 value_print(fin, P_VALUE_FMT, K->p[i][j]);
290 fprintf(fin, "]");
292 fprintf(fin, "]\n");
293 fclose(fin);
295 fout = fdopen(out, "r");
296 while (fscanf(fout, "T[%d]:={", &i) == 1) {
297 struct domain *domain = ALLOC(struct domain);
298 memset(domain, 0, sizeof(*domain));
299 domain->domain.F = calloc(vertex_words, sizeof(unsigned));
300 domain->F_len = vertex_words;
302 while (fgetc(fout) == '{') { /* '{' or closing '}' */
303 int c;
304 unsigned *F = calloc(words, sizeof(unsigned));
306 for (j = 0; j < K->NbColumns; ++j) {
307 unsigned v, shift;
308 fscanf(fout, "%d", &v);
309 shift = INT_BITS - (v % INT_BITS) - 1;
310 F[v / INT_BITS] |= 1u << shift;
311 fgetc(fout); /* , or } */
313 if (!domain->F_len)
314 free(F);
315 else if (add_vertex_to_domain(&vertices, words, F, PP->Constraints,
316 d, C->Dimension,
317 domain, options->MaxRays))
318 ++PP->nbV;
319 if ((c = fgetc(fout)) != ',') /* , or } */
320 ungetc(c, fout);
322 if (domain->F_len)
323 vertex_words = domain->F_len;
324 fgetc(fout); /* ; */
325 fgetc(fout); /* \n */
326 if (bit_count(domain->domain.F, domain->F_len) > 0)
327 add_domain(&domains, domain, vertices, C, options);
328 else {
329 options->stats->topcom_empty_chambers++;
330 Param_Domain_Free(&domain->domain);
333 fclose(fout);
335 PP->V = vertices;
336 PP->D = &domains->domain;
338 remove_empty_chambers(&PP->D, vertex_words);
340 options->MaxRays = MaxRays;
342 return PP;
345 /* Assumes M is of full row rank */
346 static Matrix *null_space(Matrix *M)
348 Matrix *H, *Q, *U;
349 Matrix *N;
350 int i;
352 left_hermite(M, &H, &Q, &U);
353 N = Matrix_Alloc(M->NbColumns, M->NbColumns - M->NbRows);
354 for (i = 0; i < N->NbRows; ++i)
355 Vector_Copy(U->p[i] + M->NbRows, N->p[i], N->NbColumns);
356 Matrix_Free(H);
357 Matrix_Free(Q);
358 Matrix_Free(U);
359 return N;
362 static void SwapColumns(Value **V, int n, int i, int j)
364 int r;
366 for (r = 0; r < n; ++r)
367 value_swap(V[r][i], V[r][j]);
370 static int is_unit_row(Value *row, int pos, int len)
372 if (!value_one_p(row[pos]) && !value_mone_p(row[pos]))
373 return 0;
374 return First_Non_Zero(row+pos+1, len-(pos+1)) == -1;
377 /* C is assumed to be the "true" context, i.e., it has been intersected
378 * with the projection of P onto the parameter space.
379 * Furthermore, P and C are assumed to be full-dimensional.
381 Param_Polyhedron *TC_P2PP(Polyhedron *P, Polyhedron *C,
382 struct barvinok_options *options)
384 unsigned nparam = C->Dimension;
385 unsigned nvar = P->Dimension - C->Dimension;
386 Matrix *M;
387 int i, j, d;
388 Matrix *H, *U, *Q;
389 Matrix *A;
390 Matrix *K;
391 int rows;
392 Param_Polyhedron *PP;
394 assert(P->NbEq == 0);
395 assert(C->NbEq == 0);
396 /* move constraints only involving parameters down
397 * and move unit vectors (if there are any) to the right place.
399 for (d = 0, j = P->NbConstraints; d < j; ++d) {
400 int index;
401 index = First_Non_Zero(P->Constraint[d]+1, nvar);
402 if (index != -1) {
403 if (index != d &&
404 is_unit_row(P->Constraint[d]+1, index, nvar)) {
405 Vector_Exchange(P->Constraint[d], P->Constraint[index],
406 P->Dimension+2);
407 if (index > d)
408 --d;
410 continue;
412 while (d < --j && First_Non_Zero(P->Constraint[j]+1, nvar) == -1)
414 if (d >= j)
415 break;
416 Vector_Exchange(P->Constraint[d], P->Constraint[j], P->Dimension+2);
418 M = Matrix_Alloc(d+nvar, nvar);
419 for (j = 0; j < d; ++j)
420 Vector_Copy(P->Constraint[j]+1, M->p[j], nvar);
422 neg_left_hermite(M, &H, &Q, &U);
423 Matrix_Free(M);
424 Matrix_Free(Q);
425 Matrix_Free(U);
426 H->NbRows -= nvar;
428 /* Rearrange rows such that top of H is lower diagonal and
429 * add extra unit vector rows that are positive linear
430 * combinations of two or more of the top rows.
432 for (i = 0; i < H->NbColumns; ++i) {
433 for (j = i; j < H->NbRows; ++j)
434 if (value_notzero_p(H->p[j][i]))
435 break;
436 if (j != i) {
437 Vector_Exchange(P->Constraint[i], P->Constraint[j], P->Dimension+2);
438 Vector_Exchange(H->p[i], H->p[j], H->NbColumns);
440 if (First_Non_Zero(H->p[i], i) != -1)
441 value_set_si(H->p[H->NbRows++][i], 1);
444 rows = H->NbRows-nvar;
445 A = Matrix_Alloc(rows, nvar+rows);
446 for (i = nvar; i < d; ++i) {
447 Vector_Oppose(H->p[i], A->p[i-nvar], H->NbColumns);
448 value_set_si(A->p[i-nvar][i], 1);
450 for (; i < H->NbRows; ++i) {
451 j = First_Non_Zero(H->p[i], nvar);
452 assert(j != -1);
453 Vector_Oppose(H->p[j], A->p[i-nvar], H->NbColumns);
454 value_set_si(A->p[i-nvar][i], 1);
455 SwapColumns(A->p, A->NbRows, i, j);
457 Matrix_Free(H);
458 K = null_space(A);
459 Matrix_Free(A);
460 /* Ignore extra constraints */
461 K->NbRows = d;
462 PP = points2triangs(K, P, C, options);
463 Matrix_Free(K);
464 return PP;