1 #include <barvinok/util.h>
2 #include <barvinok/options.h>
5 /* If a vertex is described by A x + B p + c = 0, then
6 * M = [A B] and we want to compute a linear transformation L such
7 * that H L = A and H \Z contains both A \Z and B \Z.
9 * [ A B ] = [ H 0 ] [ U_11 U_12 ]
12 * U_11 is the required linear transformation.
13 * Note that this also works if M has more rows than there are variables,
14 * i.e., if some rows in M are linear combinations of other rows.
15 * These extra rows only affect and H and not U.
17 static Lattice
*extract_lattice(Matrix
*M
, unsigned nvar
)
20 Matrix
*H
, *Q
, *U
, *Li
;
24 left_hermite(M
, &H
, &Q
, &U
);
27 Li
= Matrix_Alloc(nvar
+1, nvar
+1);
28 L
= Matrix_Alloc(nvar
+1, nvar
+1);
29 value_set_si(Li
->p
[nvar
][nvar
], 1);
31 for (row
= 0; row
< nvar
; ++row
)
32 Vector_Copy(Q
->p
[row
], Li
->p
[row
], nvar
);
36 ok
= Matrix_Inverse(Li
, L
);
43 /* Returns the smallest (wrt inclusion) lattice that contains both X and Y */
44 static Lattice
*LatticeJoin(Lattice
*X
, Lattice
*Y
)
47 int dim
= X
->NbRows
-1;
51 Matrix
*M
, *H
, *U
, *Q
;
53 assert(X
->NbColumns
-1 == dim
);
54 assert(Y
->NbRows
-1 == dim
);
55 assert(Y
->NbColumns
-1 == dim
);
60 M
= Matrix_Alloc(dim
, 2*dim
);
61 value_lcm(X
->p
[dim
][dim
], Y
->p
[dim
][dim
], &lcm
);
63 value_division(tmp
, lcm
, X
->p
[dim
][dim
]);
64 for (i
= 0; i
< dim
; ++i
)
65 Vector_Scale(X
->p
[i
], M
->p
[i
], tmp
, dim
);
66 value_division(tmp
, lcm
, Y
->p
[dim
][dim
]);
67 for (i
= 0; i
< dim
; ++i
)
68 Vector_Scale(Y
->p
[i
], M
->p
[i
]+dim
, tmp
, dim
);
70 left_hermite(M
, &H
, &Q
, &U
);
75 L
= Matrix_Alloc(dim
+1, dim
+1);
76 value_assign(L
->p
[dim
][dim
], lcm
);
77 for (i
= 0; i
< dim
; ++i
)
78 Vector_Copy(H
->p
[i
], L
->p
[i
], dim
);
86 static void Param_Vertex_Common_Denominator(Param_Vertices
*V
)
92 assert(V
->Vertex
->NbRows
> 0);
93 dim
= V
->Vertex
->NbColumns
-2;
97 value_assign(lcm
, V
->Vertex
->p
[0][dim
+1]);
98 for (i
= 1; i
< V
->Vertex
->NbRows
; ++i
)
99 value_lcm(V
->Vertex
->p
[i
][dim
+1], lcm
, &lcm
);
101 for (i
= 0; i
< V
->Vertex
->NbRows
; ++i
) {
102 if (value_eq(V
->Vertex
->p
[i
][dim
+1], lcm
))
104 value_division(V
->Vertex
->p
[i
][dim
+1], lcm
, V
->Vertex
->p
[i
][dim
+1]);
105 Vector_Scale(V
->Vertex
->p
[i
], V
->Vertex
->p
[i
],
106 V
->Vertex
->p
[i
][dim
+1], dim
+1);
107 value_assign(V
->Vertex
->p
[i
][dim
+1], lcm
);
113 static void Param_Vertex_Image(Param_Vertices
*V
, Matrix
*T
)
115 unsigned nvar
= V
->Vertex
->NbRows
;
116 unsigned nparam
= V
->Vertex
->NbColumns
- 2;
120 Param_Vertex_Common_Denominator(V
);
121 Vertex
= Matrix_Alloc(V
->Vertex
->NbRows
, V
->Vertex
->NbColumns
);
122 Matrix_Product(T
, V
->Vertex
, Vertex
);
123 for (i
= 0; i
< nvar
; ++i
) {
124 value_assign(Vertex
->p
[i
][nparam
+1], V
->Vertex
->p
[i
][nparam
+1]);
125 Vector_Normalize(Vertex
->p
[i
], nparam
+2);
127 Matrix_Free(V
->Vertex
);
131 /* Scales the parametric polyhedron with constraints *P and vertices PP
132 * such that the number of integer points can be represented by a polynomial.
133 * Both *P and P->Vertex are adapted according to the scaling.
134 * The scaling factor is returned in *det.
135 * The enumerator of the scaled parametric polyhedron should be divided
136 * by this number to obtain an approximation of the enumerator of the
137 * original parametric polyhedron.
139 * The algorithm is described in "Approximating Ehrhart Polynomials using
140 * affine transformations" by B. Meister.
142 void Param_Polyhedron_Scale_Integer_Slow(Param_Polyhedron
*PP
, Polyhedron
**P
,
143 Value
*det
, unsigned MaxRays
)
146 unsigned dim
= (*P
)->Dimension
;
149 Lattice
*L
= NULL
, *Li
;
158 nparam
= PP
->V
->Vertex
->NbColumns
- 2;
161 for (V
= PP
->V
; V
; V
= V
->next
) {
165 unsigned char *supporting
;
167 supporting
= supporting_constraints(*P
, V
, &n
);
168 M
= Matrix_Alloc(n
, (*P
)->Dimension
);
169 for (i
= 0, j
= 0; i
< (*P
)->NbConstraints
; ++i
)
171 Vector_Copy((*P
)->Constraint
[i
]+1, M
->p
[j
++], (*P
)->Dimension
);
173 L2
= extract_lattice(M
, nvar
);
179 Lattice
*L3
= LatticeJoin(L
, L2
);
186 /* apply the variable expansion to the polyhedron (constraints) */
187 expansion
= Matrix_Alloc(nvar
+ nparam
+ 1, nvar
+ nparam
+ 1);
188 for (i
= 0; i
< nvar
; ++i
)
189 Vector_Copy(L
->p
[i
], expansion
->p
[i
], nvar
);
190 for (i
= nvar
; i
< nvar
+nparam
+1; ++i
)
191 value_assign(expansion
->p
[i
][i
], L
->p
[nvar
][nvar
]);
193 *P
= Polyhedron_Preimage(*P
, expansion
, MaxRays
);
194 Matrix_Free(expansion
);
196 /* apply the variable expansion to the parametric vertices */
197 Li
= Matrix_Alloc(nvar
+1, nvar
+1);
198 ok
= Matrix_Inverse(L
, Li
);
201 assert(value_one_p(Li
->p
[nvar
][nvar
]));
202 T
= Matrix_Alloc(nvar
, nvar
);
203 value_set_si(*det
, 1);
204 for (i
= 0; i
< nvar
; ++i
) {
205 value_multiply(*det
, *det
, Li
->p
[i
][i
]);
206 Vector_Copy(Li
->p
[i
], T
->p
[i
], nvar
);
209 for (V
= PP
->V
; V
; V
= V
->next
)
210 Param_Vertex_Image(V
, T
);
214 /* Scales the parametric polyhedron with constraints *P and vertices PP
215 * such that the number of integer points can be represented by a polynomial.
216 * Both *P and P->Vertex are adapted according to the scaling.
217 * The scaling factor is returned in *det.
218 * The enumerator of the scaled parametric polyhedron should be divided
219 * by this number to obtain an approximation of the enumerator of the
220 * original parametric polyhedron.
222 * The algorithm is described in "Approximating Ehrhart Polynomials using
223 * affine transformations" by B. Meister.
225 void Param_Polyhedron_Scale_Integer_Fast(Param_Polyhedron
*PP
, Polyhedron
**P
,
226 Value
*det
, unsigned MaxRays
)
229 int nb_param
, nb_vars
;
232 Value global_var_lcm
;
235 value_set_si(*det
, 1);
239 nb_param
= PP
->D
->Domain
->Dimension
;
240 nb_vars
= PP
->V
->Vertex
->NbRows
;
242 /* Scan the vertices and make an orthogonal expansion of the variable
244 /* a- prepare the array of common denominators */
245 denoms
= Vector_Alloc(nb_vars
);
246 value_init(global_var_lcm
);
248 /* b- scan the vertices and compute the variables' global lcms */
249 for (V
= PP
->V
; V
; V
= V
->next
)
250 for (i
= 0; i
< nb_vars
; i
++)
251 Lcm3(denoms
->p
[i
], V
->Vertex
->p
[i
][nb_param
+1], &denoms
->p
[i
]);
253 value_set_si(global_var_lcm
, 1);
254 for (i
= 0; i
< nb_vars
; i
++) {
255 value_multiply(*det
, *det
, denoms
->p
[i
]);
256 Lcm3(global_var_lcm
, denoms
->p
[i
], &global_var_lcm
);
260 for (V
= PP
->V
; V
; V
= V
->next
)
261 for (i
= 0; i
< nb_vars
; i
++) {
262 Vector_Scale(V
->Vertex
->p
[i
], V
->Vertex
->p
[i
], denoms
->p
[i
], nb_param
+1);
263 Vector_Normalize(V
->Vertex
->p
[i
], nb_param
+2);
266 /* the expansion can be actually writen as global_var_lcm.L^{-1} */
267 /* this is equivalent to multiply the rows of P by denoms_det */
268 for (i
= 0; i
< nb_vars
; i
++)
269 value_division(denoms
->p
[i
], global_var_lcm
, denoms
->p
[i
]);
271 /* OPT : we could use a vector instead of a diagonal matrix here (c- and d-).*/
272 /* c- make the quick expansion matrix */
273 expansion
= Matrix_Alloc(nb_vars
+nb_param
+1, nb_vars
+nb_param
+1);
274 for (i
= 0; i
< nb_vars
; i
++)
275 value_assign(expansion
->p
[i
][i
], denoms
->p
[i
]);
276 for (i
= nb_vars
; i
< nb_vars
+nb_param
+1; i
++)
277 value_assign(expansion
->p
[i
][i
], global_var_lcm
);
279 /* d- apply the variable expansion to the polyhedron */
281 *P
= Polyhedron_Preimage(*P
, expansion
, MaxRays
);
283 Matrix_Free(expansion
);
284 value_clear(global_var_lcm
);
288 /* adapted from mpolyhedron_inflate in PolyLib */
289 static Polyhedron
*Polyhedron_Inflate(Polyhedron
*P
, unsigned nparam
,
293 int nvar
= P
->Dimension
- nparam
;
294 Matrix
*C
= Polyhedron2Constraints(P
);
299 /* subtract the sum of the negative coefficients of each inequality */
300 for (i
= 0; i
< C
->NbRows
; ++i
) {
301 value_set_si(sum
, 0);
302 for (j
= 0; j
< nvar
; ++j
)
303 if (value_neg_p(C
->p
[i
][1+j
]))
304 value_addto(sum
, sum
, C
->p
[i
][1+j
]);
305 value_subtract(C
->p
[i
][1+P
->Dimension
], C
->p
[i
][1+P
->Dimension
], sum
);
308 P2
= Constraints2Polyhedron(C
, MaxRays
);
313 /* adapted from mpolyhedron_deflate in PolyLib */
314 static Polyhedron
*Polyhedron_Deflate(Polyhedron
*P
, unsigned nparam
,
318 int nvar
= P
->Dimension
- nparam
;
319 Matrix
*C
= Polyhedron2Constraints(P
);
324 /* subtract the sum of the positive coefficients of each inequality */
325 for (i
= 0; i
< C
->NbRows
; ++i
) {
326 value_set_si(sum
, 0);
327 for (j
= 0; j
< nvar
; ++j
)
328 if (value_pos_p(C
->p
[i
][1+j
]))
329 value_addto(sum
, sum
, C
->p
[i
][1+j
]);
330 value_subtract(C
->p
[i
][1+P
->Dimension
], C
->p
[i
][1+P
->Dimension
], sum
);
333 P2
= Constraints2Polyhedron(C
, MaxRays
);
338 Polyhedron
*scale_init(Polyhedron
*P
, Polyhedron
*C
, struct scale_data
*scaling
,
339 struct barvinok_options
*options
)
341 unsigned nparam
= C
->Dimension
;
343 value_init(scaling
->det
);
344 value_set_si(scaling
->det
, 1);
345 scaling
->save_approximation
= options
->polynomial_approximation
;
347 if (options
->polynomial_approximation
== BV_APPROX_SIGN_NONE
||
348 options
->polynomial_approximation
== BV_APPROX_SIGN_APPROX
)
351 if (options
->polynomial_approximation
== BV_APPROX_SIGN_UPPER
)
352 P
= Polyhedron_Inflate(P
, nparam
, options
->MaxRays
);
353 if (options
->polynomial_approximation
== BV_APPROX_SIGN_LOWER
)
354 P
= Polyhedron_Deflate(P
, nparam
, options
->MaxRays
);
356 /* Don't deflate/inflate again (on this polytope) */
357 options
->polynomial_approximation
= BV_APPROX_SIGN_NONE
;
362 Polyhedron
*scale(Param_Polyhedron
*PP
, Polyhedron
*P
,
363 struct scale_data
*scaling
, int free_P
,
364 struct barvinok_options
*options
)
368 if (options
->scale_flags
& BV_APPROX_SCALE_FAST
)
369 Param_Polyhedron_Scale_Integer_Fast(PP
, &T
, &scaling
->det
, options
->MaxRays
);
371 Param_Polyhedron_Scale_Integer_Slow(PP
, &T
, &scaling
->det
, options
->MaxRays
);
377 void scale_finish(evalue
*e
, struct scale_data
*scaling
,
378 struct barvinok_options
*options
)
380 if (value_notone_p(scaling
->det
))
381 evalue_div(e
, scaling
->det
);
382 value_clear(scaling
->det
);
383 /* reset options that may have been changed */
384 options
->polynomial_approximation
= scaling
->save_approximation
;