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"
9 #define ALLOC(type) (type*)malloc(sizeof(type))
10 #define ALLOCN(type,n) (type*)malloc((n) * sizeof(type))
12 /* returns an evalue that corresponds to
16 static evalue
*term(int param
, Value c
, Value den
)
18 evalue
*EP
= ALLOC(evalue
);
20 value_set_si(EP
->d
,0);
21 EP
->x
.p
= new_enode(polynomial
, 2, param
+ 1);
22 evalue_set_si(&EP
->x
.p
->arr
[0], 0, 1);
23 value_init(EP
->x
.p
->arr
[1].x
.n
);
24 value_assign(EP
->x
.p
->arr
[1].d
, den
);
25 value_assign(EP
->x
.p
->arr
[1].x
.n
, c
);
29 /* Computes an evalue representation of a coordinate
32 static evalue
*vertex2evalue(Value
*vertex
, int nparam
)
35 evalue
*E
= ALLOC(evalue
);
37 evalue_set(E
, vertex
[nparam
], vertex
[nparam
+1]);
38 for (i
= 0; i
< nparam
; ++i
) {
39 evalue
*t
= term(i
, vertex
[i
], vertex
[nparam
+1]);
47 static void matrix_print(evalue
***matrix
, int dim
, int *cols
,
52 for (i
= 0; i
< dim
; ++i
)
53 for (j
= 0; j
< dim
; ++j
) {
54 int k
= cols
? cols
[j
] : j
;
55 fprintf(stderr
, "%d %d: ", i
, j
);
56 print_evalue(stderr
, matrix
[i
][k
], param_names
);
57 fprintf(stderr
, "\n");
61 /* Compute determinant using Laplace's formula.
62 * In particular, the determinant is expanded along the last row.
63 * The cols array is a list of columns that remain in the currect submatrix.
65 static evalue
*determinant_cols(evalue
***matrix
, int dim
, int *cols
)
73 evalue_copy(det
, matrix
[0][cols
[0]]);
78 evalue_set_si(&mone
, -1, 1);
81 int *newcols
= ALLOCN(int, dim
-1);
82 for (j
= 1; j
< dim
; ++j
)
83 newcols
[j
-1] = cols
[j
];
84 for (j
= 0; j
< dim
; ++j
) {
86 newcols
[j
-1] = cols
[j
-1];
87 tmp
= determinant_cols(matrix
, dim
-1, newcols
);
88 emul(matrix
[dim
-1][cols
[j
]], tmp
);
95 free_evalue_refs(tmp
);
100 free_evalue_refs(&mone
);
105 static evalue
*determinant(evalue
***matrix
, int dim
)
108 int *cols
= ALLOCN(int, dim
);
111 for (i
= 0; i
< dim
; ++i
)
114 det
= determinant_cols(matrix
, dim
, cols
);
121 /* Compute the facet of P that saturates constraint c.
123 static Polyhedron
*facet(Polyhedron
*P
, int c
, unsigned MaxRays
)
126 Vector
*row
= Vector_Alloc(1+P
->Dimension
+1);
127 Vector_Copy(P
->Constraint
[c
]+1, row
->p
+1, P
->Dimension
+1);
128 F
= AddConstraints(row
->p
, 1, P
, MaxRays
);
133 /* Compute a dummy Param_Domain that contains all vertices of Param_Domain D
134 * (which contains the vertices of P) that lie on the facet obtain by
135 * saturating constraint c of P
137 static Param_Domain
*face_vertices(Param_Polyhedron
*PP
, Param_Domain
*D
,
138 Polyhedron
*P
, int c
)
142 Param_Domain
*FD
= ALLOC(Param_Domain
);
146 nv
= (PP
->nbV
- 1)/(8*sizeof(int)) + 1;
147 FD
->F
= ALLOCN(unsigned, nv
);
148 memset(FD
->F
, 0, nv
* sizeof(unsigned));
150 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
) /* _i, _ix, _bx internal counters */
152 unsigned char *supporting
= supporting_constraints(P
, V
, &n
);
156 END_FORALL_PVertex_in_ParamPolyhedron
;
161 /* Substitute parameters by the corresponding element in subs
163 static evalue
*evalue_substitute(evalue
*e
, evalue
**subs
)
169 if (value_notzero_p(e
->d
)) {
175 assert(e
->x
.p
->type
== polynomial
);
178 for (i
= e
->x
.p
->size
-1; i
> 0; --i
) {
179 c
= evalue_substitute(&e
->x
.p
->arr
[i
], subs
);
183 emul(subs
[e
->x
.p
->pos
-1], res
);
185 c
= evalue_substitute(&e
->x
.p
->arr
[0], subs
);
193 /* Plug in the parametric vertex V in the constraint constraint.
194 * The result is stored in row, with the denominator in position 0.
196 static void Param_Inner_Product(Value
*constraint
, Matrix
*Vertex
,
199 unsigned nparam
= Vertex
->NbColumns
- 2;
200 unsigned nvar
= Vertex
->NbRows
;
204 value_set_si(row
[0], 1);
205 Vector_Set(row
+1, 0, nparam
+1);
210 for (j
= 0 ; j
< nvar
; ++j
) {
211 value_set_si(tmp
, 1);
212 value_assign(tmp2
, constraint
[1+j
]);
213 if (value_ne(row
[0], Vertex
->p
[j
][nparam
+1])) {
214 value_assign(tmp
, row
[0]);
215 value_lcm(row
[0], Vertex
->p
[j
][nparam
+1], &row
[0]);
216 value_division(tmp
, row
[0], tmp
);
217 value_multiply(tmp2
, tmp2
, row
[0]);
218 value_division(tmp2
, tmp2
, Vertex
->p
[j
][nparam
+1]);
220 Vector_Combine(row
+1, Vertex
->p
[j
], row
+1, tmp
, tmp2
, nparam
+1);
222 value_set_si(tmp
, 1);
223 Vector_Combine(row
+1, constraint
+1+nvar
, row
+1, tmp
, row
[0], nparam
+1);
229 struct parameter_point
{
234 struct parameter_point
*parameter_point_new(unsigned nparam
)
236 struct parameter_point
*point
= ALLOC(struct parameter_point
);
237 point
->coord
= Vector_Alloc(nparam
+1);
242 evalue
**parameter_point_evalue(struct parameter_point
*point
)
245 unsigned nparam
= point
->coord
->Size
-1;
250 point
->e
= ALLOCN(evalue
*, nparam
);
251 for (j
= 0; j
< nparam
; ++j
) {
252 point
->e
[j
] = ALLOC(evalue
);
253 value_init(point
->e
[j
]->d
);
254 evalue_set(point
->e
[j
], point
->coord
->p
[j
], point
->coord
->p
[nparam
]);
260 void parameter_point_free(struct parameter_point
*point
)
263 unsigned nparam
= point
->coord
->Size
-1;
265 Vector_Free(point
->coord
);
268 for (i
= 0; i
< nparam
; ++i
) {
269 free_evalue_refs(point
->e
[i
]);
277 /* Computes point in pameter space where polyhedron is non-empty.
278 * For each of the parametric vertices, and each of the facets
279 * not (always) containing the vertex, we remove the parameter
280 * values for which the facet does contain the vertex.
282 static struct parameter_point
*non_empty_point(Param_Polyhedron
*PP
,
283 Param_Domain
*D
, Polyhedron
*P
, Polyhedron
*C
, unsigned MaxRays
)
286 unsigned dim
= P
->Dimension
;
287 unsigned nparam
= C
->Dimension
;
288 unsigned nvar
= dim
- nparam
;
289 Polyhedron
*RD
, *cut
, *tmp
;
291 struct parameter_point
*point
;
293 unsigned cut_MaxRays
= MaxRays
;
296 nv
= (PP
->nbV
- 1)/(8*sizeof(int)) + 1;
298 POL_UNSET(cut_MaxRays
, POL_INTEGER
);
300 M
= Matrix_Alloc(1, nparam
+2);
302 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
) /* _ix, _bx internal counters */
303 for (i
= P
->NbEq
; i
< P
->NbConstraints
; ++i
) {
304 if (First_Non_Zero(P
->Constraint
[i
]+1, nvar
) == -1)
306 Param_Inner_Product(P
->Constraint
[i
], V
->Vertex
, M
->p
[0]);
307 if (First_Non_Zero(M
->p
[0]+1, nparam
) == -1)
309 * or non-supporting facet independent of params
312 value_set_si(M
->p
[0][0], 0);
313 cut
= Constraints2Polyhedron(M
, cut_MaxRays
);
314 tmp
= DomainDifference(RD
, cut
, MaxRays
);
318 Polyhedron_Free(cut
);
322 END_FORALL_PVertex_in_ParamPolyhedron
;
325 POL_ENSURE_VERTICES(RD
);
329 point
= parameter_point_new(nparam
);
330 for (i
= 0; i
< RD
->NbRays
; ++i
)
331 if (value_notzero_p(RD
->Ray
[i
][1+nparam
]))
333 assert(i
< RD
->NbRays
);
334 Vector_Copy(RD
->Ray
[i
]+1, point
->coord
->p
, nparam
+1);
343 static Matrix
*barycenter(Param_Polyhedron
*PP
, Param_Domain
*D
)
346 Matrix
*center
= NULL
;
356 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
)
359 center
= Matrix_Copy(V
->Vertex
);
360 nparam
= center
->NbColumns
- 2;
362 for (i
= 0; i
< center
->NbRows
; ++i
) {
363 value_assign(fc
, center
->p
[i
][nparam
+1]);
364 value_lcm(fc
, V
->Vertex
->p
[i
][nparam
+1],
365 ¢er
->p
[i
][nparam
+1]);
366 value_division(fc
, center
->p
[i
][nparam
+1], fc
);
367 value_division(fv
, center
->p
[i
][nparam
+1],
368 V
->Vertex
->p
[i
][nparam
+1]);
369 Vector_Combine(center
->p
[i
], V
->Vertex
->p
[i
], center
->p
[i
],
373 END_FORALL_PVertex_in_ParamPolyhedron
;
378 value_set_si(denom
, nbV
);
379 for (i
= 0; i
< center
->NbRows
; ++i
) {
380 value_multiply(center
->p
[i
][nparam
+1], center
->p
[i
][nparam
+1], denom
);
381 Vector_Normalize(center
->p
[i
], nparam
+2);
388 /* Compute dim! times the volume of polyhedron F in Param_Domain D.
389 * If F is a simplex, then the volume is computed of a recursive pyramid
390 * over F with the points already in matrix.
391 * Otherwise, the barycenter of F is added to matrix and the function
392 * is called recursively on the facets of F.
394 * The first row of matrix contain the _negative_ of the first point.
395 * The remaining rows of matrix contain the distance of the corresponding
396 * point to the first point.
398 static evalue
*volume_in_domain(Param_Polyhedron
*PP
, Param_Domain
*D
,
399 unsigned dim
, evalue
***matrix
,
400 struct parameter_point
*point
, Polyhedron
*C
,
401 int row
, Polyhedron
*F
,
402 struct barvinok_options
*options
);
404 static evalue
*volume_triangulate(Param_Polyhedron
*PP
, Param_Domain
*D
,
405 unsigned dim
, evalue
***matrix
,
406 struct parameter_point
*point
, Polyhedron
*C
,
407 int row
, Polyhedron
*F
,
408 struct barvinok_options
*options
)
415 unsigned cut_MaxRays
= options
->MaxRays
;
416 unsigned nparam
= C
->Dimension
;
419 POL_UNSET(cut_MaxRays
, POL_INTEGER
);
422 evalue_set_si(&mone
, -1, 1);
424 center
= barycenter(PP
, D
);
425 for (j
= 0; j
< dim
; ++j
)
426 matrix
[row
][j
] = vertex2evalue(center
->p
[j
], center
->NbColumns
- 2);
429 for (j
= 0; j
< dim
; ++j
)
430 emul(&mone
, matrix
[row
][j
]);
432 for (j
= 0; j
< dim
; ++j
)
433 eadd(matrix
[0][j
], matrix
[row
][j
]);
437 M
= Matrix_Alloc(1, nparam
+2);
440 POL_ENSURE_FACETS(F
);
441 for (j
= F
->NbEq
; j
< F
->NbConstraints
; ++j
) {
445 if (First_Non_Zero(F
->Constraint
[j
]+1, dim
) == -1)
452 Param_Inner_Product(F
->Constraint
[j
], center
, M
->p
[0]);
453 pos
= First_Non_Zero(M
->p
[0]+1, nparam
+1);
455 /* barycenter always lies on facet */
460 value_set_si(M
->p
[0][0], 0);
461 cut
= Constraints2Polyhedron(M
, cut_MaxRays
);
462 FC
= DomainDifference(C
, cut
, options
->MaxRays
);
463 Polyhedron_Free(cut
);
464 POL_ENSURE_VERTICES(FC
);
466 /* barycenter lies on facet for all parameters in C */
472 FF
= facet(F
, j
, options
->MaxRays
);
473 FD
= face_vertices(PP
, D
, F
, j
);
474 tmp
= volume_in_domain(PP
, FD
, dim
, matrix
, point
, FC
,
482 free_evalue_refs(tmp
);
486 Param_Domain_Free(FD
);
493 for (j
= 0; j
< dim
; ++j
) {
494 free_evalue_refs(matrix
[row
][j
]);
495 free(matrix
[row
][j
]);
498 free_evalue_refs(&mone
);
502 static evalue
*volume_simplex(Param_Polyhedron
*PP
, Param_Domain
*D
,
503 unsigned dim
, evalue
***matrix
,
504 struct parameter_point
*point
,
505 int row
, unsigned MaxRays
)
513 return evalue_zero();
516 evalue_set_si(&mone
, -1, 1);
519 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
) /* _ix, _bx internal counters */
520 for (j
= 0; j
< dim
; ++j
) {
521 matrix
[i
][j
] = vertex2evalue(V
->Vertex
->p
[j
],
522 V
->Vertex
->NbColumns
- 2);
524 emul(&mone
, matrix
[i
][j
]);
526 eadd(matrix
[0][j
], matrix
[i
][j
]);
529 END_FORALL_PVertex_in_ParamPolyhedron
;
531 vol
= determinant(matrix
+1, dim
);
533 val
= evalue_substitute(vol
, parameter_point_evalue(point
));
535 assert(value_notzero_p(val
->d
));
536 assert(value_notzero_p(val
->x
.n
));
537 if (value_neg_p(val
->x
.n
))
540 free_evalue_refs(val
);
543 for (i
= row
; i
< dim
+1; ++i
) {
544 for (j
= 0; j
< dim
; ++j
) {
545 free_evalue_refs(matrix
[i
][j
]);
550 free_evalue_refs(&mone
);
555 static evalue
*volume_triangulate_lift(Param_Polyhedron
*PP
, Param_Domain
*D
,
556 unsigned dim
, evalue
***matrix
,
557 struct parameter_point
*point
,
558 int row
, unsigned MaxRays
)
560 const static int MAX_TRY
=10;
565 Matrix
*FixedRays
, *M
;
573 nv
= (PP
->nbV
- 1)/(8*sizeof(int)) + 1;
574 SD
.F
= ALLOCN(unsigned, nv
);
576 FixedRays
= Matrix_Alloc(PP
->nbV
+1, 1+dim
+2);
578 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
)
579 unsigned nparam
= V
->Vertex
->NbColumns
- 2;
580 Param_Vertex_Common_Denominator(V
);
581 for (i
= 0; i
< V
->Vertex
->NbRows
; ++i
) {
582 value_multiply(FixedRays
->p
[nbV
][1+i
], V
->Vertex
->p
[i
][nparam
],
583 point
->coord
->p
[nparam
]);
584 Inner_Product(V
->Vertex
->p
[i
], point
->coord
->p
, nparam
,
585 &FixedRays
->p
[nbV
][1+dim
]);
586 value_addto(FixedRays
->p
[nbV
][1+i
], FixedRays
->p
[nbV
][1+i
],
587 FixedRays
->p
[nbV
][1+dim
]);
589 value_multiply(FixedRays
->p
[nbV
][1+dim
+1], V
->Vertex
->p
[0][nparam
+1],
590 point
->coord
->p
[nparam
]);
591 value_set_si(FixedRays
->p
[nbV
][0], 1);
593 END_FORALL_PVertex_in_ParamPolyhedron
;
594 value_set_si(FixedRays
->p
[nbV
][0], 1);
595 value_set_si(FixedRays
->p
[nbV
][1+dim
], 1);
596 FixedRays
->NbRows
= nbV
+1;
601 /* Usually vol should still be NULL */
603 free_evalue_refs(vol
);
609 assert(t
<= MAX_TRY
);
612 for (i
= 0; i
< nbV
; ++i
)
613 value_set_si(FixedRays
->p
[i
][1+dim
], random_int((t
+1)*dim
*nbV
)+1);
615 M
= Matrix_Copy(FixedRays
);
616 L
= Rays2Polyhedron(M
, MaxRays
);
619 POL_ENSURE_FACETS(L
);
620 for (i
= 0; i
< L
->NbConstraints
; ++i
) {
622 /* Ignore perpendicular facets, i.e., facets with 0 z-coordinate */
623 if (value_negz_p(L
->Constraint
[i
][1+dim
]))
626 memset(SD
.F
, 0, nv
* sizeof(unsigned));
629 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
) /* _ix, _bx internal */
630 Inner_Product(FixedRays
->p
[nbV
]+1, L
->Constraint
[i
]+1, dim
+2, &tmp
);
631 if (value_zero_p(tmp
)) {
638 END_FORALL_PVertex_in_ParamPolyhedron
;
639 assert(r
== (dim
-row
)+1);
641 s
= volume_simplex(PP
, &SD
, dim
, matrix
, point
, row
, MaxRays
);
651 Matrix_Free(FixedRays
);
658 static evalue
*volume_in_domain(Param_Polyhedron
*PP
, Param_Domain
*D
,
659 unsigned dim
, evalue
***matrix
,
660 struct parameter_point
*point
, Polyhedron
*C
,
661 int row
, Polyhedron
*F
,
662 struct barvinok_options
*options
)
667 int point_computed
= 0;
670 point
= non_empty_point(PP
, D
, F
, C
, options
->MaxRays
);
676 FORALL_PVertex_in_ParamPolyhedron(V
, D
, PP
)
678 END_FORALL_PVertex_in_ParamPolyhedron
;
680 if (nbV
> (dim
-row
) + 1) {
681 if (point
&& options
->volume_triangulate_lift
)
682 vol
= volume_triangulate_lift(PP
, D
, dim
, matrix
, point
,
683 row
, options
->MaxRays
);
685 vol
= volume_triangulate(PP
, D
, dim
, matrix
, point
, C
,
688 assert(nbV
== (dim
-row
) + 1);
689 vol
= volume_simplex(PP
, D
, dim
, matrix
, point
, row
, options
->MaxRays
);
693 parameter_point_free(point
);
698 evalue
* Param_Polyhedron_Volume(Polyhedron
*P
, Polyhedron
* C
,
699 struct barvinok_options
*options
)
702 unsigned nparam
= C
->Dimension
;
703 unsigned nvar
= P
->Dimension
- C
->Dimension
;
704 Param_Polyhedron
*PP
;
705 unsigned PP_MaxRays
= options
->MaxRays
;
711 struct section
{ Polyhedron
*D
; evalue
*E
; } *s
;
713 Param_Domain
*D
, *next
;
716 if (options
->polynomial_approximation
== BV_APPROX_SIGN_NONE
)
717 options
->polynomial_approximation
= BV_APPROX_SIGN_APPROX
;
719 if (options
->polynomial_approximation
!= BV_APPROX_SIGN_APPROX
) {
720 int pa
= options
->polynomial_approximation
;
721 assert(pa
== BV_APPROX_SIGN_UPPER
|| pa
== BV_APPROX_SIGN_LOWER
);
723 P
= Polyhedron_Flate(P
, nparam
, pa
== BV_APPROX_SIGN_UPPER
,
726 /* Don't deflate/inflate again (on this polytope) */
727 options
->polynomial_approximation
= BV_APPROX_SIGN_APPROX
;
728 vol
= barvinok_enumerate_with_options(P
, C
, options
);
729 options
->polynomial_approximation
= pa
;
735 if (PP_MaxRays
& POL_NO_DUAL
)
738 MaxRays
= options
->MaxRays
;
739 POL_UNSET(options
->MaxRays
, POL_INTEGER
);
742 Factorial(nvar
, &fact
);
744 PP
= Polyhedron2Param_Domain(P
, C
, PP_MaxRays
);
746 for (nd
= 0, D
= PP
->D
; D
; ++nd
, D
= D
->next
);
747 s
= ALLOCN(struct section
, nd
);
748 fVD
= ALLOCN(Polyhedron
*, nd
);
750 matrix
= ALLOCN(evalue
**, nvar
+1);
751 for (i
= 0; i
< nvar
+1; ++i
)
752 matrix
[i
] = ALLOCN(evalue
*, nvar
);
754 for (nd
= 0, D
= PP
->D
; D
; D
= next
) {
755 Polyhedron
*rVD
= reduce_domain(D
->Domain
, NULL
, NULL
, fVD
, nd
, options
);
762 CA
= align_context(D
->Domain
, P
->Dimension
, MaxRays
);
763 F
= DomainIntersection(P
, CA
, options
->MaxRays
);
767 s
[nd
].E
= volume_in_domain(PP
, D
, nvar
, matrix
, NULL
, rVD
,
770 evalue_div(s
[nd
].E
, fact
);
774 options
->MaxRays
= MaxRays
;
778 value_set_si(vol
->d
, 0);
781 evalue_set_si(vol
, 0, 1);
783 vol
->x
.p
= new_enode(partition
, 2*nd
, C
->Dimension
);
784 for (i
= 0; i
< nd
; ++i
) {
785 EVALUE_SET_DOMAIN(vol
->x
.p
->arr
[2*i
], s
[i
].D
);
786 value_clear(vol
->x
.p
->arr
[2*i
+1].d
);
787 vol
->x
.p
->arr
[2*i
+1] = *s
[i
].E
;
795 for (i
= 0; i
< nvar
+1; ++i
)
798 Param_Polyhedron_Free(PP
);