1 #include <barvinok/polylib.h>
2 #include <barvinok/barvinok.h>
3 #include <barvinok/options.h>
4 #include <barvinok/util.h>
5 #include "reduce_domain.h"
6 #include "param_util.h"
9 #define ALLOC(type) (type*)malloc(sizeof(type))
10 #define ALLOCN(type,n) (type*)malloc((n) * sizeof(type))
12 /* Computes an evalue representation of a coordinate
15 static evalue
*vertex2evalue(Value
*vertex
, int nparam
)
17 return affine2evalue(vertex
, vertex
[nparam
+1], nparam
);
20 static void matrix_print(evalue
***matrix
, int dim
, int *cols
,
25 for (i
= 0; i
< dim
; ++i
)
26 for (j
= 0; j
< dim
; ++j
) {
27 int k
= cols
? cols
[j
] : j
;
28 fprintf(stderr
, "%d %d: ", i
, j
);
29 print_evalue(stderr
, matrix
[i
][k
], param_names
);
30 fprintf(stderr
, "\n");
34 /* Compute determinant using Laplace's formula.
35 * In particular, the determinant is expanded along the last row.
36 * The cols array is a list of columns that remain in the currect submatrix.
38 static evalue
*determinant_cols(evalue
***matrix
, int dim
, int *cols
)
48 evalue_copy(det
, matrix
[0][cols
[0]]);
53 evalue_set_si(&mone
, -1, 1);
55 newcols
= ALLOCN(int, dim
-1);
56 for (j
= 1; j
< dim
; ++j
)
57 newcols
[j
-1] = cols
[j
];
58 for (j
= 0; j
< dim
; ++j
) {
60 newcols
[j
-1] = cols
[j
-1];
61 tmp
= determinant_cols(matrix
, dim
-1, newcols
);
62 emul(matrix
[dim
-1][cols
[j
]], tmp
);
69 free_evalue_refs(tmp
);
74 free_evalue_refs(&mone
);
79 static evalue
*determinant(evalue
***matrix
, int dim
)
82 int *cols
= ALLOCN(int, dim
);
85 for (i
= 0; i
< dim
; ++i
)
88 det
= determinant_cols(matrix
, dim
, cols
);
95 /* Compute the facet of P that saturates constraint c.
97 static Polyhedron
*facet(Polyhedron
*P
, int c
, unsigned MaxRays
)
100 Vector
*row
= Vector_Alloc(1+P
->Dimension
+1);
101 Vector_Copy(P
->Constraint
[c
]+1, row
->p
+1, P
->Dimension
+1);
102 F
= AddConstraints(row
->p
, 1, P
, MaxRays
);
107 /* Plug in the parametric vertex V in the constraint constraint.
108 * The result is stored in row, with the denominator in position 0.
110 static void Param_Inner_Product(Value
*constraint
, Matrix
*Vertex
,
113 unsigned nparam
= Vertex
->NbColumns
- 2;
114 unsigned nvar
= Vertex
->NbRows
;
118 value_set_si(row
[0], 1);
119 Vector_Set(row
+1, 0, nparam
+1);
124 for (j
= 0 ; j
< nvar
; ++j
) {
125 value_set_si(tmp
, 1);
126 value_assign(tmp2
, constraint
[1+j
]);
127 if (value_ne(row
[0], Vertex
->p
[j
][nparam
+1])) {
128 value_assign(tmp
, row
[0]);
129 value_lcm(row
[0], Vertex
->p
[j
][nparam
+1], &row
[0]);
130 value_division(tmp
, row
[0], tmp
);
131 value_multiply(tmp2
, tmp2
, row
[0]);
132 value_division(tmp2
, tmp2
, Vertex
->p
[j
][nparam
+1]);
134 Vector_Combine(row
+1, Vertex
->p
[j
], row
+1, tmp
, tmp2
, nparam
+1);
136 value_set_si(tmp
, 1);
137 Vector_Combine(row
+1, constraint
+1+nvar
, row
+1, tmp
, row
[0], nparam
+1);
143 /* Compute a dummy Param_Domain that contains all vertices of Param_Domain D
144 * (which contains the vertices of P) that lie on the facet obtain by
145 * saturating constraint c of P
147 static Param_Domain
*face_vertices(Param_Polyhedron
*PP
, Param_Domain
*D
,
148 Polyhedron
*P
, int c
)
152 unsigned nparam
= PP
->V
->Vertex
->NbColumns
-2;
153 Vector
*row
= Vector_Alloc(1+nparam
+1);
154 Param_Domain
*FD
= ALLOC(Param_Domain
);
158 nv
= (PP
->nbV
- 1)/(8*sizeof(int)) + 1;
159 FD
->F
= ALLOCN(unsigned, nv
);
160 memset(FD
->F
, 0, nv
* sizeof(unsigned));
162 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
) /* _i, _ix, _bx internal counters */
164 Param_Inner_Product(P
->Constraint
[c
], V
->Vertex
, row
->p
);
165 if (First_Non_Zero(row
->p
+1, nparam
+1) == -1)
167 END_FORALL_PVertex_in_ParamPolyhedron
;
174 /* Substitute parameters by the corresponding element in subs
176 static evalue
*evalue_substitute_new(evalue
*e
, evalue
**subs
)
182 if (value_notzero_p(e
->d
)) {
188 assert(e
->x
.p
->type
== polynomial
);
191 for (i
= e
->x
.p
->size
-1; i
> 0; --i
) {
192 c
= evalue_substitute_new(&e
->x
.p
->arr
[i
], subs
);
196 emul(subs
[e
->x
.p
->pos
-1], res
);
198 c
= evalue_substitute_new(&e
->x
.p
->arr
[0], subs
);
206 struct parameter_point
{
211 struct parameter_point
*parameter_point_new(unsigned nparam
)
213 struct parameter_point
*point
= ALLOC(struct parameter_point
);
214 point
->coord
= Vector_Alloc(nparam
+1);
219 evalue
**parameter_point_evalue(struct parameter_point
*point
)
222 unsigned nparam
= point
->coord
->Size
-1;
227 point
->e
= ALLOCN(evalue
*, nparam
);
228 for (j
= 0; j
< nparam
; ++j
) {
229 point
->e
[j
] = ALLOC(evalue
);
230 value_init(point
->e
[j
]->d
);
231 evalue_set(point
->e
[j
], point
->coord
->p
[j
], point
->coord
->p
[nparam
]);
237 void parameter_point_free(struct parameter_point
*point
)
240 unsigned nparam
= point
->coord
->Size
-1;
242 Vector_Free(point
->coord
);
245 for (i
= 0; i
< nparam
; ++i
) {
246 free_evalue_refs(point
->e
[i
]);
254 /* Computes point in pameter space where polyhedron is non-empty.
256 static struct parameter_point
*non_empty_point(Param_Domain
*D
)
258 unsigned nparam
= D
->Domain
->Dimension
;
259 struct parameter_point
*point
;
262 v
= inner_point(D
->Domain
);
263 point
= parameter_point_new(nparam
);
264 Vector_Copy(v
->p
+1, point
->coord
->p
, nparam
+1);
270 static Matrix
*barycenter(Param_Polyhedron
*PP
, Param_Domain
*D
)
273 Matrix
*center
= NULL
;
283 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
)
286 center
= Matrix_Copy(V
->Vertex
);
287 nparam
= center
->NbColumns
- 2;
289 for (i
= 0; i
< center
->NbRows
; ++i
) {
290 value_assign(fc
, center
->p
[i
][nparam
+1]);
291 value_lcm(fc
, V
->Vertex
->p
[i
][nparam
+1],
292 ¢er
->p
[i
][nparam
+1]);
293 value_division(fc
, center
->p
[i
][nparam
+1], fc
);
294 value_division(fv
, center
->p
[i
][nparam
+1],
295 V
->Vertex
->p
[i
][nparam
+1]);
296 Vector_Combine(center
->p
[i
], V
->Vertex
->p
[i
], center
->p
[i
],
300 END_FORALL_PVertex_in_ParamPolyhedron
;
305 value_set_si(denom
, nbV
);
306 for (i
= 0; i
< center
->NbRows
; ++i
) {
307 value_multiply(center
->p
[i
][nparam
+1], center
->p
[i
][nparam
+1], denom
);
308 Vector_Normalize(center
->p
[i
], nparam
+2);
315 static Matrix
*triangulation_vertex(Param_Polyhedron
*PP
, Param_Domain
*D
,
320 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
)
322 END_FORALL_PVertex_in_ParamPolyhedron
;
328 /* Compute dim! times the volume of polyhedron F in Param_Domain D.
329 * If F is a simplex, then the volume is computed of a recursive pyramid
330 * over F with the points already in matrix.
331 * Otherwise, the barycenter of F is added to matrix and the function
332 * is called recursively on the facets of F.
334 * The first row of matrix contain the _negative_ of the first point.
335 * The remaining rows of matrix contain the distance of the corresponding
336 * point to the first point.
338 static evalue
*volume_in_domain(Param_Polyhedron
*PP
, Param_Domain
*D
,
339 unsigned dim
, evalue
***matrix
,
340 struct parameter_point
*point
,
341 int row
, Polyhedron
*F
,
342 struct barvinok_options
*options
);
344 static evalue
*volume_triangulate(Param_Polyhedron
*PP
, Param_Domain
*D
,
345 unsigned dim
, evalue
***matrix
,
346 struct parameter_point
*point
,
347 int row
, Polyhedron
*F
,
348 struct barvinok_options
*options
)
355 unsigned cut_MaxRays
= options
->MaxRays
;
356 unsigned nparam
= PP
->V
->Vertex
->NbColumns
-2;
359 POL_UNSET(cut_MaxRays
, POL_INTEGER
);
362 evalue_set_si(&mone
, -1, 1);
364 if (options
->volume_triangulate
== BV_VOL_BARYCENTER
)
365 center
= barycenter(PP
, D
);
367 center
= triangulation_vertex(PP
, D
, F
);
368 for (j
= 0; j
< dim
; ++j
)
369 matrix
[row
][j
] = vertex2evalue(center
->p
[j
], center
->NbColumns
- 2);
370 if (options
->volume_triangulate
== BV_VOL_BARYCENTER
)
373 v
= Vector_Alloc(1+nparam
+1);
376 for (j
= 0; j
< dim
; ++j
)
377 emul(&mone
, matrix
[row
][j
]);
379 for (j
= 0; j
< dim
; ++j
)
380 eadd(matrix
[0][j
], matrix
[row
][j
]);
384 POL_ENSURE_FACETS(F
);
385 for (j
= F
->NbEq
; j
< F
->NbConstraints
; ++j
) {
388 if (First_Non_Zero(F
->Constraint
[j
]+1, dim
) == -1)
390 if (options
->volume_triangulate
!= BV_VOL_BARYCENTER
) {
391 Param_Inner_Product(F
->Constraint
[j
], center
, v
->p
);
392 if (First_Non_Zero(v
->p
+1, nparam
+1) == -1)
395 FF
= facet(F
, j
, options
->MaxRays
);
396 FD
= face_vertices(PP
, D
, F
, j
);
397 tmp
= volume_in_domain(PP
, FD
, dim
, matrix
, point
,
403 free_evalue_refs(tmp
);
407 Param_Domain_Free(FD
);
410 if (options
->volume_triangulate
!= BV_VOL_BARYCENTER
)
413 for (j
= 0; j
< dim
; ++j
) {
414 free_evalue_refs(matrix
[row
][j
]);
415 free(matrix
[row
][j
]);
418 free_evalue_refs(&mone
);
422 static evalue
*volume_simplex(Param_Polyhedron
*PP
, Param_Domain
*D
,
423 unsigned dim
, evalue
***matrix
,
424 struct parameter_point
*point
,
425 int row
, struct barvinok_options
*options
)
432 options
->stats
->volume_simplices
++;
435 evalue_set_si(&mone
, -1, 1);
438 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
) /* _ix, _bx internal counters */
439 for (j
= 0; j
< dim
; ++j
) {
440 matrix
[i
][j
] = vertex2evalue(V
->Vertex
->p
[j
],
441 V
->Vertex
->NbColumns
- 2);
443 emul(&mone
, matrix
[i
][j
]);
445 eadd(matrix
[0][j
], matrix
[i
][j
]);
448 END_FORALL_PVertex_in_ParamPolyhedron
;
450 vol
= determinant(matrix
+1, dim
);
452 val
= evalue_substitute_new(vol
, parameter_point_evalue(point
));
454 assert(value_notzero_p(val
->d
));
455 assert(value_notzero_p(val
->x
.n
));
456 if (value_neg_p(val
->x
.n
))
459 free_evalue_refs(val
);
462 for (i
= row
; i
< dim
+1; ++i
) {
463 for (j
= 0; j
< dim
; ++j
) {
464 free_evalue_refs(matrix
[i
][j
]);
469 free_evalue_refs(&mone
);
474 static evalue
*volume_triangulate_lift(Param_Polyhedron
*PP
, Param_Domain
*D
,
475 unsigned dim
, evalue
***matrix
,
476 struct parameter_point
*point
,
477 int row
, struct barvinok_options
*options
)
479 const static int MAX_TRY
=10;
484 Matrix
*FixedRays
, *M
;
492 nv
= (PP
->nbV
- 1)/(8*sizeof(int)) + 1;
493 SD
.F
= ALLOCN(unsigned, nv
);
495 FixedRays
= Matrix_Alloc(PP
->nbV
+1, 1+dim
+2);
497 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
)
498 unsigned nparam
= V
->Vertex
->NbColumns
- 2;
499 Param_Vertex_Common_Denominator(V
);
500 for (i
= 0; i
< V
->Vertex
->NbRows
; ++i
) {
501 value_multiply(FixedRays
->p
[nbV
][1+i
], V
->Vertex
->p
[i
][nparam
],
502 point
->coord
->p
[nparam
]);
503 Inner_Product(V
->Vertex
->p
[i
], point
->coord
->p
, nparam
,
504 &FixedRays
->p
[nbV
][1+dim
]);
505 value_addto(FixedRays
->p
[nbV
][1+i
], FixedRays
->p
[nbV
][1+i
],
506 FixedRays
->p
[nbV
][1+dim
]);
508 value_multiply(FixedRays
->p
[nbV
][1+dim
+1], V
->Vertex
->p
[0][nparam
+1],
509 point
->coord
->p
[nparam
]);
510 value_set_si(FixedRays
->p
[nbV
][0], 1);
512 END_FORALL_PVertex_in_ParamPolyhedron
;
513 value_set_si(FixedRays
->p
[nbV
][0], 1);
514 value_set_si(FixedRays
->p
[nbV
][1+dim
], 1);
515 FixedRays
->NbRows
= nbV
+1;
520 /* Usually vol should still be NULL */
522 free_evalue_refs(vol
);
528 assert(t
<= MAX_TRY
);
531 for (i
= 0; i
< nbV
; ++i
)
532 value_set_si(FixedRays
->p
[i
][1+dim
], random_int((t
+1)*dim
*nbV
)+1);
534 M
= Matrix_Copy(FixedRays
);
535 L
= Rays2Polyhedron(M
, options
->MaxRays
);
538 POL_ENSURE_FACETS(L
);
539 for (i
= 0; i
< L
->NbConstraints
; ++i
) {
541 /* Ignore perpendicular facets, i.e., facets with 0 z-coordinate */
542 if (value_negz_p(L
->Constraint
[i
][1+dim
]))
545 memset(SD
.F
, 0, nv
* sizeof(unsigned));
548 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
) /* _ix, _bx internal */
549 Inner_Product(FixedRays
->p
[nbV
]+1, L
->Constraint
[i
]+1, dim
+2, &tmp
);
550 if (value_zero_p(tmp
)) {
557 END_FORALL_PVertex_in_ParamPolyhedron
;
558 assert(r
== (dim
-row
)+1);
560 s
= volume_simplex(PP
, &SD
, dim
, matrix
, point
, row
, options
);
570 Matrix_Free(FixedRays
);
577 static evalue
*volume_in_domain(Param_Polyhedron
*PP
, Param_Domain
*D
,
578 unsigned dim
, evalue
***matrix
,
579 struct parameter_point
*point
,
580 int row
, Polyhedron
*F
,
581 struct barvinok_options
*options
)
590 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
)
592 END_FORALL_PVertex_in_ParamPolyhedron
;
594 if (nbV
> (dim
-row
) + 1) {
595 if (options
->volume_triangulate
== BV_VOL_LIFT
)
596 vol
= volume_triangulate_lift(PP
, D
, dim
, matrix
, point
,
599 vol
= volume_triangulate(PP
, D
, dim
, matrix
, point
,
602 assert(nbV
== (dim
-row
) + 1);
603 vol
= volume_simplex(PP
, D
, dim
, matrix
, point
, row
, options
);
609 evalue
* Param_Polyhedron_Volume(Polyhedron
*P
, Polyhedron
* C
,
610 struct barvinok_options
*options
)
613 unsigned nparam
= C
->Dimension
;
614 unsigned nvar
= P
->Dimension
- C
->Dimension
;
615 Param_Polyhedron
*PP
;
616 unsigned PP_MaxRays
= options
->MaxRays
;
622 struct section
{ Polyhedron
*D
; evalue
*E
; } *s
;
626 if (options
->polynomial_approximation
== BV_APPROX_SIGN_NONE
)
627 options
->polynomial_approximation
= BV_APPROX_SIGN_APPROX
;
629 if (options
->polynomial_approximation
!= BV_APPROX_SIGN_APPROX
) {
630 int pa
= options
->polynomial_approximation
;
631 assert(pa
== BV_APPROX_SIGN_UPPER
|| pa
== BV_APPROX_SIGN_LOWER
);
633 P
= Polyhedron_Flate(P
, nparam
, pa
== BV_APPROX_SIGN_UPPER
,
636 /* Don't deflate/inflate again (on this polytope) */
637 options
->polynomial_approximation
= BV_APPROX_SIGN_APPROX
;
638 vol
= barvinok_enumerate_with_options(P
, C
, options
);
639 options
->polynomial_approximation
= pa
;
645 TC
= true_context(P
, C
, options
->MaxRays
);
647 if (PP_MaxRays
& POL_NO_DUAL
)
650 MaxRays
= options
->MaxRays
;
651 POL_UNSET(options
->MaxRays
, POL_INTEGER
);
654 Factorial(nvar
, &fact
);
656 PP
= Polyhedron2Param_Domain(P
, C
, PP_MaxRays
);
658 for (nd
= 0, D
= PP
->D
; D
; ++nd
, D
= D
->next
);
659 s
= ALLOCN(struct section
, nd
);
661 matrix
= ALLOCN(evalue
**, nvar
+1);
662 for (i
= 0; i
< nvar
+1; ++i
)
663 matrix
[i
] = ALLOCN(evalue
*, nvar
);
665 FORALL_REDUCED_DOMAIN(PP
, TC
, nd
, options
, i
, D
, rVD
)
667 struct parameter_point
*point
;
669 CA
= align_context(D
->Domain
, P
->Dimension
, MaxRays
);
670 F
= DomainIntersection(P
, CA
, options
->MaxRays
);
673 point
= non_empty_point(D
);
675 s
[i
].E
= volume_in_domain(PP
, D
, nvar
, matrix
, point
, 0, F
, options
);
677 parameter_point_free(point
);
678 evalue_div(s
[i
].E
, fact
);
679 END_FORALL_REDUCED_DOMAIN
680 options
->MaxRays
= MaxRays
;
685 value_set_si(vol
->d
, 0);
688 evalue_set_si(vol
, 0, 1);
690 vol
->x
.p
= new_enode(partition
, 2*nd
, C
->Dimension
);
691 for (i
= 0; i
< nd
; ++i
) {
692 EVALUE_SET_DOMAIN(vol
->x
.p
->arr
[2*i
], s
[i
].D
);
693 value_clear(vol
->x
.p
->arr
[2*i
+1].d
);
694 vol
->x
.p
->arr
[2*i
+1] = *s
[i
].E
;
700 for (i
= 0; i
< nvar
+1; ++i
)
703 Param_Polyhedron_Free(PP
);