short_rat::print: correctly print out terms with a zero coefficient
[barvinok.git] / barvinok_enumerate.cc
blob66eeba9cc80f2070c949ab238bf14bd8a37b3835
1 #include <assert.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <barvinok/evalue.h>
5 #include <barvinok/util.h>
6 #include <barvinok/barvinok.h>
7 #include "argp.h"
8 #include "progname.h"
9 #include "verify.h"
10 #include "verif_ehrhart.h"
11 #include "remove_equalities.h"
12 #include "evalue_convert.h"
13 #include "conversion.h"
15 #undef CS /* for Solaris 10 */
17 using std::cout;
18 using std::endl;
20 /* The input of this example program is the same as that of testehrhart
21 * in the PolyLib distribution, i.e., a polytope in combined
22 * data and parameter space, a context polytope in parameter space
23 * and (optionally) the names of the parameters.
24 * Both polytopes are in PolyLib notation.
27 #define PRINT_STATS (BV_OPT_LAST+1)
29 struct argp_option argp_options[] = {
30 { "size", 'S' },
31 { "series", 's', 0, 0, "compute rational generating function" },
32 { "explicit", 'e', 0, 0, "convert rgf to psp" },
33 { "verbose", 'v' },
34 { "print-stats", PRINT_STATS, 0, 0 },
35 { 0 }
38 struct arguments {
39 int size;
40 int series;
41 int function;
42 int verbose;
43 int print_stats;
44 struct verify_options verify;
45 struct convert_options convert;
48 static error_t parse_opt(int key, char *arg, struct argp_state *state)
50 struct arguments *options = (struct arguments*) state->input;
52 switch (key) {
53 case ARGP_KEY_INIT:
54 state->child_inputs[0] = options->verify.barvinok;
55 state->child_inputs[1] = &options->verify;
56 state->child_inputs[2] = &options->convert;
57 options->size = 0;
58 options->series = 0;
59 options->function = 0;
60 options->verbose = 0;
61 options->print_stats = 0;
62 break;
63 case PRINT_STATS:
64 options->print_stats = 1;
65 break;
66 case 'S':
67 options->size = 1;
68 break;
69 case 'e':
70 options->function = 1;
71 /* fall through */
72 case 's':
73 options->series = 1;
74 break;
75 case 'v':
76 options->verbose = 1;
77 break;
78 default:
79 return ARGP_ERR_UNKNOWN;
81 return 0;
84 struct skewed_gen_fun {
85 gen_fun *gf;
86 /* maps original space to space in which gf is defined */
87 Matrix *T;
88 /* equalities in the original space that need to be satisfied for
89 * gf to be valid
91 Matrix *eq;
92 /* divisibilities in the original space that need to be satisfied for
93 * gf to be valid
95 Matrix *div;
97 skewed_gen_fun(gen_fun *gf, Matrix *T, Matrix *eq, Matrix *div) :
98 gf(gf), T(T), eq(eq), div(div) {}
99 ~skewed_gen_fun() {
100 if (T)
101 Matrix_Free(T);
102 if (eq)
103 Matrix_Free(eq);
104 if (div)
105 Matrix_Free(div);
106 delete gf;
109 void print(std::ostream& os, unsigned int nparam, char **param_name) const;
110 operator evalue *() const {
111 assert(T == NULL && eq == NULL); /* other cases not supported for now */
112 return *gf;
114 void coefficient(Value* params, Value* c, barvinok_options *options) const;
117 void skewed_gen_fun::print(std::ostream& os, unsigned int nparam,
118 char **param_name) const
120 mat_ZZ m;
121 if (T) {
122 os << "T:" << endl;
123 matrix2zz(T, m, T->NbRows, T->NbColumns);
124 os << m << endl;
126 if (eq) {
127 os << "eq:" << endl;
128 matrix2zz(eq, m, eq->NbRows, eq->NbColumns);
129 os << m << endl;
131 if (div) {
132 os << "div:" << endl;
133 matrix2zz(div, m, div->NbRows, div->NbColumns);
134 os << m << endl;
136 gf->print(os, nparam, param_name);
139 void skewed_gen_fun::coefficient(Value* params, Value* c,
140 barvinok_options *options) const
142 if (eq) {
143 for (int i = 0; i < eq->NbRows; ++i) {
144 Inner_Product(eq->p[i]+1, params, eq->NbColumns-2, eq->p[i]);
145 if (value_notzero_p(eq->p[i][0])) {
146 value_set_si(*c, 0);
147 return;
151 if (div) {
152 Value tmp;
153 value_init(tmp);
154 for (int i = 0; i < div->NbRows; ++i) {
155 Inner_Product(div->p[i], params, div->NbColumns-1, &tmp);
156 if (!mpz_divisible_p(tmp, div->p[i][div->NbColumns-1])) {
157 value_set_si(*c, 0);
158 return;
161 value_clear(tmp);
164 ZZ coeff;
165 if (!T)
166 coeff = gf->coefficient(params, options);
167 else {
168 Vector *p2 = Vector_Alloc(T->NbRows);
169 Matrix_Vector_Product(T, params, p2->p);
170 if (value_notone_p(p2->p[T->NbRows-1]))
171 Vector_AntiScale(p2->p, p2->p, p2->p[T->NbRows-1], T->NbRows);
172 coeff = gf->coefficient(p2->p, options);
173 Vector_Free(p2);
176 zz2value(coeff, *c);
179 static int check_series(Polyhedron *S, Polyhedron *CS, skewed_gen_fun *gf,
180 int nparam, int pos, Value *z, verify_options *options)
182 int k;
183 Value c, tmp;
184 Value LB, UB;
186 value_init(c);
187 value_init(tmp);
188 value_init(LB);
189 value_init(UB);
191 if (pos == nparam) {
192 /* Computes the coefficient */
193 gf->coefficient(&z[S->Dimension-nparam+1], &c, options->barvinok);
195 /* if c=0 we may be out of context. */
196 /* scanning is useless in this case*/
198 if (options->print_all) {
199 printf("EP( ");
200 value_print(stdout,VALUE_FMT,z[S->Dimension-nparam+1]);
201 for(k=S->Dimension-nparam+2;k<=S->Dimension;++k) {
202 printf(", ");
203 value_print(stdout,VALUE_FMT,z[k]);
205 printf(" ) = ");
206 value_print(stdout,VALUE_FMT,c);
207 printf(" ");
210 /* Manually count the number of points */
211 count_points(1,S,z,&tmp);
212 if (options->print_all) {
213 printf(", count = ");
214 value_print(stdout, P_VALUE_FMT, tmp);
215 printf(". ");
218 if (value_ne(tmp,c)) {
219 printf("\n");
220 fflush(stdout);
221 fprintf(stderr,"Error !\n");
222 fprintf(stderr,"EP( ");
223 value_print(stderr,VALUE_FMT,z[S->Dimension-nparam+1]);
224 for (k=S->Dimension-nparam+2;k<=S->Dimension;++k) {
225 fprintf(stderr,", ");
226 value_print(stderr,VALUE_FMT,z[k]);
228 fprintf(stderr," ) should be ");
229 value_print(stderr,VALUE_FMT,tmp);
230 fprintf(stderr,", while EP eval gives ");
231 value_print(stderr,VALUE_FMT,c);
232 fprintf(stderr,".\n");
233 if (!options->continue_on_error) {
234 value_clear(c); value_clear(tmp);
235 return 0;
237 } else if (options->print_all)
238 printf("OK.\n");
239 } else {
240 int ok =
241 !(lower_upper_bounds(1+pos, CS, &z[S->Dimension-nparam], &LB, &UB));
242 assert(ok);
243 for (value_assign(tmp,LB); value_le(tmp,UB); value_increment(tmp,tmp)) {
244 if (!options->print_all) {
245 k = VALUE_TO_INT(tmp);
246 if(!pos && !(k % options->st)) {
247 printf("o");
248 fflush(stdout);
251 value_assign(z[pos+S->Dimension-nparam+1],tmp);
252 if (!check_series(S, CS->next, gf, nparam, pos+1, z, options)) {
253 value_clear(c); value_clear(tmp);
254 value_clear(LB);
255 value_clear(UB);
256 return(0);
259 value_set_si(z[pos+S->Dimension-nparam+1],0);
262 value_clear(c);
263 value_clear(tmp);
264 value_clear(LB);
265 value_clear(UB);
266 return 1;
269 static int verify(Polyhedron *P, Polyhedron *C, evalue *EP, skewed_gen_fun *gf,
270 arguments *options)
272 Polyhedron *CS, *S;
273 Vector *p;
274 int result = 0;
276 CS = check_poly_context_scan(P, &C, C->Dimension, &options->verify);
278 p = Vector_Alloc(P->Dimension+2);
279 value_set_si(p->p[P->Dimension+1], 1);
281 /* S = scanning list of polyhedra */
282 S = Polyhedron_Scan(P, C, options->verify.barvinok->MaxRays);
284 check_poly_init(C, &options->verify);
286 /******* CHECK NOW *********/
287 if (S) {
288 if (!options->series || options->function) {
289 if (!check_poly_EP(S, CS, EP, 0, C->Dimension, 0, p->p,
290 &options->verify))
291 result = -1;
292 } else {
293 if (!check_series(S, CS, gf, C->Dimension, 0, p->p, &options->verify))
294 result = -1;
296 Domain_Free(S);
299 if (result == -1)
300 fprintf(stderr,"Check failed !\n");
302 if (!options->verify.print_all)
303 printf( "\n" );
305 Vector_Free(p);
306 if (CS) {
307 Domain_Free(CS);
308 Domain_Free(C);
311 return result;
314 /* frees M and Minv */
315 static void apply_transformation(Polyhedron **P, Polyhedron **C,
316 bool free_P, bool free_C,
317 Matrix *M, Matrix *Minv, Matrix **inv,
318 barvinok_options *options)
320 Polyhedron *T;
321 Matrix *M2;
323 M2 = align_matrix(M, (*P)->Dimension + 1);
324 T = *P;
325 *P = Polyhedron_Preimage(*P, M2, options->MaxRays);
326 if (free_P)
327 Polyhedron_Free(T);
328 Matrix_Free(M2);
330 T = *C;
331 *C = Polyhedron_Preimage(*C, M, options->MaxRays);
332 if (free_C)
333 Polyhedron_Free(T);
335 Matrix_Free(M);
337 if (*inv) {
338 Matrix *T = *inv;
339 *inv = Matrix_Alloc(Minv->NbRows, T->NbColumns);
340 Matrix_Product(Minv, T, *inv);
341 Matrix_Free(T);
342 Matrix_Free(Minv);
343 } else
344 *inv = Minv;
347 /* Since we have "compressed" the parameters (in case there were
348 * any equalities), the result is independent of the coordinates in the
349 * coordinate subspace spanned by the lines. We can therefore assume
350 * these coordinates are zero and compute the inverse image of the map
351 * from a lower dimensional space that adds zeros in the appropriate
352 * places.
354 static void remove_lines(Polyhedron *C, Matrix **M, Matrix **Minv)
356 Matrix *L = Matrix_Alloc(C->Dimension+1, C->Dimension+1);
357 for (int r = 0; r < C->NbBid; ++r)
358 Vector_Copy(C->Ray[r]+1, L->p[r], C->Dimension);
359 unimodular_complete(L, C->NbBid);
360 assert(value_one_p(L->p[C->Dimension][C->Dimension]));
361 assert(First_Non_Zero(L->p[C->Dimension], C->Dimension) == -1);
362 Matrix_Transposition(L);
363 assert(First_Non_Zero(L->p[C->Dimension], C->Dimension) == -1);
365 *M = Matrix_Alloc(C->Dimension+1, C->Dimension-C->NbBid+1);
366 for (int i = 0; i < C->Dimension+1; ++i)
367 Vector_Copy(L->p[i]+C->NbBid, (*M)->p[i], C->Dimension-C->NbBid+1);
369 Matrix *Linv = Matrix_Alloc(C->Dimension+1, C->Dimension+1);
370 int ok = Matrix_Inverse(L, Linv);
371 assert(ok);
372 Matrix_Free(L);
374 *Minv = Matrix_Alloc(C->Dimension-C->NbBid+1, C->Dimension+1);
375 for (int i = C->NbBid; i < C->Dimension+1; ++i)
376 Vector_AntiScale(Linv->p[i], (*Minv)->p[i-C->NbBid],
377 Linv->p[C->Dimension][C->Dimension], C->Dimension+1);
378 Matrix_Free(Linv);
381 static skewed_gen_fun *series(Polyhedron *P, Polyhedron* C,
382 barvinok_options *options)
384 Polyhedron *C1, *C2;
385 gen_fun *gf;
386 Matrix *inv = NULL;
387 Matrix *eq = NULL;
388 Matrix *div = NULL;
389 Polyhedron *PT = P;
391 /* Compute true context */
392 C1 = Polyhedron_Project(P, C->Dimension);
393 C2 = DomainIntersection(C, C1, options->MaxRays);
394 Polyhedron_Free(C1);
396 POL_ENSURE_VERTICES(C2);
397 if (C2->NbBid != 0) {
398 Polyhedron *T;
399 Matrix *M, *Minv, *M2;
400 Matrix *CP;
401 if (C2->NbEq || P->NbEq) {
402 /* We remove all equalities to be sure all lines are unit vectors */
403 Polyhedron *CT = C2;
404 remove_all_equalities(&PT, &CT, &CP, NULL, C2->Dimension,
405 options->MaxRays);
406 if (CT != C2) {
407 Polyhedron_Free(C2);
408 C2 = CT;
410 if (CP) {
411 inv = left_inverse(CP, &eq);
412 Matrix_Free(CP);
414 int d = 0;
415 Value tmp;
416 value_init(tmp);
417 div = Matrix_Alloc(inv->NbRows-1, inv->NbColumns+1);
418 for (int i = 0; i < inv->NbRows-1; ++i) {
419 Vector_Gcd(inv->p[i], inv->NbColumns, &tmp);
420 if (mpz_divisible_p(tmp,
421 inv->p[inv->NbRows-1][inv->NbColumns-1]))
422 continue;
423 Vector_Copy(inv->p[i], div->p[d], inv->NbColumns);
424 value_assign(div->p[d][inv->NbColumns],
425 inv->p[inv->NbRows-1][inv->NbColumns-1]);
426 ++d;
428 value_clear(tmp);
430 if (!d) {
431 Matrix_Free(div);
432 div = NULL;
433 } else
434 div->NbRows = d;
437 POL_ENSURE_VERTICES(C2);
439 if (C2->NbBid) {
440 Matrix *M, *Minv;
441 remove_lines(C2, &M, &Minv);
442 apply_transformation(&PT, &C2, PT != P, C2 != C, M, Minv, &inv,
443 options);
446 POL_ENSURE_VERTICES(C2);
447 if (!Polyhedron_has_revlex_positive_rays(C2, C2->Dimension)) {
448 Polyhedron *T;
449 Matrix *Constraints;
450 Matrix *H, *Q, *U;
451 Constraints = Matrix_Alloc(C2->NbConstraints, C2->Dimension+1);
452 for (int i = 0; i < C2->NbConstraints; ++i)
453 Vector_Copy(C2->Constraint[i]+1, Constraints->p[i], C2->Dimension);
454 left_hermite(Constraints, &H, &Q, &U);
455 Matrix_Free(Constraints);
456 /* flip rows of Q */
457 for (int i = 0; i < C2->Dimension/2; ++i)
458 Vector_Exchange(Q->p[i], Q->p[C2->Dimension-1-i], C2->Dimension);
459 Matrix_Free(H);
460 Matrix_Free(U);
461 Matrix *M = Matrix_Alloc(C2->Dimension+1, C2->Dimension+1);
462 U = Matrix_Copy(Q);
463 int ok = Matrix_Inverse(U, M);
464 assert(ok);
465 Matrix_Free(U);
467 apply_transformation(&PT, &C2, PT != P, C2 != C, M, Q, &inv, options);
469 gf = barvinok_series_with_options(PT, C2, options);
470 Polyhedron_Free(C2);
471 if (PT != P)
472 Polyhedron_Free(PT);
473 return new skewed_gen_fun(gf, inv, eq, div);
476 int main(int argc, char **argv)
478 Polyhedron *A, *C;
479 Matrix *M;
480 evalue *EP = NULL;
481 skewed_gen_fun *gf = NULL;
482 char **param_name;
483 int print_solution = 1;
484 int result = 0;
485 struct arguments options;
486 static struct argp_child argp_children[] = {
487 { &barvinok_argp, 0, 0, 0 },
488 { &verify_argp, 0, "verification", BV_GRP_LAST+1 },
489 { &convert_argp, 0, "output conversion", BV_GRP_LAST+2 },
490 { 0 }
492 static struct argp argp = { argp_options, parse_opt, 0, 0, argp_children };
493 struct barvinok_options *bv_options = barvinok_options_new_with_defaults();
495 options.verify.barvinok = bv_options;
496 set_program_name(argv[0]);
497 argp_parse(&argp, argc, argv, 0, 0, &options);
499 M = Matrix_Read();
500 assert(M);
501 A = Constraints2Polyhedron(M, bv_options->MaxRays);
502 Matrix_Free(M);
503 M = Matrix_Read();
504 assert(M);
505 C = Constraints2Polyhedron(M, bv_options->MaxRays);
506 Matrix_Free(M);
507 param_name = Read_ParamNames(stdin, C->Dimension);
509 if (options.verify.verify) {
510 verify_options_set_range(&options.verify, A->Dimension);
511 if (!options.verbose)
512 print_solution = 0;
515 if (print_solution && options.verbose) {
516 Polyhedron_Print(stdout, P_VALUE_FMT, A);
517 Polyhedron_Print(stdout, P_VALUE_FMT, C);
520 if (options.series) {
521 gf = series(A, C, bv_options);
522 if (print_solution) {
523 gf->print(cout, C->Dimension, param_name);
524 puts("");
526 if (options.function) {
527 EP = *gf;
528 if (print_solution)
529 print_evalue(stdout, EP, param_name);
531 } else {
532 EP = barvinok_enumerate_with_options(A, C, bv_options);
533 if (evalue_convert(EP, &options.convert, options.verbose, C->Dimension,
534 param_name))
535 print_solution = 0;
536 if (options.size)
537 printf("\nSize: %d\n", evalue_size(EP));
538 if (print_solution)
539 print_evalue(stdout, EP, param_name);
542 if (options.verify.verify) {
543 options.verify.params = param_name;
544 result = verify(A, C, EP, gf, &options);
547 if (gf)
548 delete gf;
549 if (EP)
550 evalue_free(EP);
552 if (options.print_stats)
553 barvinok_stats_print(options.verify.barvinok->stats, stdout);
555 Free_ParamNames(param_name, C->Dimension);
556 Polyhedron_Free(A);
557 Polyhedron_Free(C);
558 barvinok_options_free(bv_options);
559 return result;