4 #include <barvinok/genfun.h>
5 #include <barvinok/barvinok.h>
6 #include "conversion.h"
11 static int lex_cmp(mat_ZZ
& a
, mat_ZZ
& b
)
13 assert(a
.NumCols() == b
.NumCols());
14 int alen
= a
.NumRows();
15 int blen
= b
.NumRows();
16 int len
= alen
< blen
? alen
: blen
;
18 for (int i
= 0; i
< len
; ++i
) {
19 int s
= lex_cmp(a
[i
], b
[i
]);
26 static void lex_order_terms(struct short_rat
* rat
)
28 for (int i
= 0; i
< rat
->n
.power
.NumRows(); ++i
) {
30 for (int j
= i
+1; j
< rat
->n
.power
.NumRows(); ++j
)
31 if (lex_cmp(rat
->n
.power
[j
], rat
->n
.power
[m
]) < 0)
34 vec_ZZ tmp
= rat
->n
.power
[m
];
35 rat
->n
.power
[m
] = rat
->n
.power
[i
];
36 rat
->n
.power
[i
] = tmp
;
37 tmp
= rat
->n
.coeff
[m
];
38 rat
->n
.coeff
[m
] = rat
->n
.coeff
[i
];
39 rat
->n
.coeff
[i
] = tmp
;
44 void short_rat::add(short_rat
*r
)
46 for (int i
= 0; i
< r
->n
.power
.NumRows(); ++i
) {
47 int len
= n
.coeff
.NumRows();
49 for (j
= 0; j
< len
; ++j
)
50 if (r
->n
.power
[i
] == n
.power
[j
])
53 ZZ g
= GCD(r
->n
.coeff
[i
][1], n
.coeff
[j
][1]);
54 ZZ num
= n
.coeff
[j
][0] * (r
->n
.coeff
[i
][1] / g
) +
55 (n
.coeff
[j
][1] / g
) * r
->n
.coeff
[i
][0];
56 ZZ d
= n
.coeff
[j
][1] / g
* r
->n
.coeff
[i
][1];
59 n
.coeff
[j
][0] = num
/g
;
63 n
.power
[j
] = n
.power
[len
-1];
64 n
.coeff
[j
] = n
.coeff
[len
-1];
66 int dim
= n
.power
.NumCols();
67 n
.coeff
.SetDims(len
-1, 2);
68 n
.power
.SetDims(len
-1, dim
);
71 int dim
= n
.power
.NumCols();
72 n
.coeff
.SetDims(len
+1, 2);
73 n
.power
.SetDims(len
+1, dim
);
74 n
.coeff
[len
] = r
->n
.coeff
[i
];
75 n
.power
[len
] = r
->n
.power
[i
];
80 bool short_rat::reduced()
82 int dim
= n
.power
.NumCols();
83 lex_order_terms(this);
84 if (n
.power
.NumRows() % 2 == 0) {
85 if (n
.coeff
[0][0] == -n
.coeff
[1][0] &&
86 n
.coeff
[0][1] == n
.coeff
[1][1]) {
87 vec_ZZ step
= n
.power
[1] - n
.power
[0];
89 for (k
= 1; k
< n
.power
.NumRows()/2; ++k
) {
90 if (n
.coeff
[2*k
][0] != -n
.coeff
[2*k
+1][0] ||
91 n
.coeff
[2*k
][1] != n
.coeff
[2*k
+1][1])
93 if (step
!= n
.power
[2*k
+1] - n
.power
[2*k
])
96 if (k
== n
.power
.NumRows()/2) {
97 for (k
= 0; k
< d
.power
.NumRows(); ++k
)
98 if (d
.power
[k
] == step
)
100 if (k
< d
.power
.NumRows()) {
101 for (++k
; k
< d
.power
.NumRows(); ++k
)
102 d
.power
[k
-1] = d
.power
[k
];
103 d
.power
.SetDims(k
-1, dim
);
104 for (k
= 1; k
< n
.power
.NumRows()/2; ++k
) {
105 n
.coeff
[k
] = n
.coeff
[2*k
];
106 n
.power
[k
] = n
.power
[2*k
];
108 n
.coeff
.SetDims(k
, 2);
109 n
.power
.SetDims(k
, dim
);
118 void gen_fun::add(const ZZ
& cn
, const ZZ
& cd
, const vec_ZZ
& num
,
124 short_rat
* r
= new short_rat
;
125 r
->n
.coeff
.SetDims(1, 2);
127 r
->n
.coeff
[0][0] = cn
/g
;
128 r
->n
.coeff
[0][1] = cd
/g
;
129 r
->n
.power
.SetDims(1, num
.length());
133 /* Make all powers in denominator lexico-positive */
134 for (int i
= 0; i
< r
->d
.power
.NumRows(); ++i
) {
136 for (j
= 0; j
< r
->d
.power
.NumCols(); ++j
)
137 if (r
->d
.power
[i
][j
] != 0)
139 if (r
->d
.power
[i
][j
] < 0) {
140 r
->d
.power
[i
] = -r
->d
.power
[i
];
141 r
->n
.coeff
[0][0] = -r
->n
.coeff
[0][0];
142 r
->n
.power
[0] += r
->d
.power
[i
];
146 /* Order powers in denominator */
147 lex_order_rows(r
->d
.power
);
149 for (int i
= 0; i
< term
.size(); ++i
)
150 if (lex_cmp(term
[i
]->d
.power
, r
->d
.power
) == 0) {
152 if (term
[i
]->n
.coeff
.NumRows() == 0) {
154 if (i
!= term
.size()-1)
155 term
[i
] = term
[term
.size()-1];
157 } else if (term
[i
]->reduced()) {
159 /* we've modified term[i], so removed it
160 * and add it back again
163 if (i
!= term
.size()-1)
164 term
[i
] = term
[term
.size()-1];
176 void gen_fun::add(const ZZ
& cn
, const ZZ
& cd
, const gen_fun
*gf
)
179 for (int i
= 0; i
< gf
->term
.size(); ++i
) {
180 for (int j
= 0; j
< gf
->term
[i
]->n
.power
.NumRows(); ++j
) {
181 n
= cn
* gf
->term
[i
]->n
.coeff
[j
][0];
182 d
= cd
* gf
->term
[i
]->n
.coeff
[j
][1];
183 add(n
, d
, gf
->term
[i
]->n
.power
[j
], gf
->term
[i
]->d
.power
);
189 * Perform the substitution specified by CP and (map, offset)
191 * CP is a homogeneous matrix that maps a set of "compressed parameters"
192 * to the original set of parameters.
194 * This function is applied to a gen_fun computed with the compressed parameters
195 * and adapts it to refer to the original parameters.
197 * That is, if y are the compressed parameters and x = A y + b are the original
198 * parameters, then we want the coefficient of the monomial t^y in the original
199 * generating function to be the coefficient of the monomial u^x in the resulting
200 * generating function.
201 * The original generating function has the form
203 * a t^m/(1-t^n) = a t^m + a t^{m+n} + a t^{m+2n} + ...
205 * Since each term t^y should correspond to a term u^x, with x = A y + b, we want
207 * a u^{A m + b} + a u^{A (m+n) + b} + a u^{A (m+2n) +b} + ... =
209 * = a u^{A m + b}/(1-u^{A n})
211 * Therefore, we multiply the powers m and n in both numerator and denominator by A
212 * and add b to the power in the numerator.
213 * Since the above powers are stored as row vectors m^T and n^T,
214 * we compute, say, m'^T = m^T A^T to obtain m' = A m.
216 * The pair (map, offset) contains the same information as CP.
217 * map is the transpose of the linear part of CP, while offset is the constant part.
219 void gen_fun::substitute(Matrix
*CP
, const mat_ZZ
& map
, const vec_ZZ
& offset
)
221 Polyhedron
*C
= Polyhedron_Image(context
, CP
, 0);
222 Polyhedron_Free(context
);
224 for (int i
= 0; i
< term
.size(); ++i
) {
225 term
[i
]->d
.power
*= map
;
226 term
[i
]->n
.power
*= map
;
227 for (int j
= 0; j
< term
[i
]->n
.power
.NumRows(); ++j
)
228 term
[i
]->n
.power
[j
] += offset
;
232 gen_fun
*gen_fun::Hadamard_product(gen_fun
*gf
, unsigned MaxRays
)
234 Polyhedron
*C
= DomainIntersection(context
, gf
->context
, MaxRays
);
235 Polyhedron
*U
= Universe_Polyhedron(C
->Dimension
);
236 gen_fun
*sum
= new gen_fun(C
);
237 for (int i
= 0; i
< term
.size(); ++i
) {
238 for (int i2
= 0; i2
< gf
->term
.size(); ++i2
) {
239 int d
= term
[i
]->d
.power
.NumCols();
240 int k1
= term
[i
]->d
.power
.NumRows();
241 int k2
= gf
->term
[i2
]->d
.power
.NumRows();
242 assert(term
[i
]->d
.power
.NumCols() == gf
->term
[i2
]->d
.power
.NumCols());
243 for (int j
= 0; j
< term
[i
]->n
.power
.NumRows(); ++j
) {
244 for (int j2
= 0; j2
< gf
->term
[i2
]->n
.power
.NumRows(); ++j2
) {
245 Matrix
*M
= Matrix_Alloc(k1
+k2
+d
+d
, 1+k1
+k2
+d
+1);
246 for (int k
= 0; k
< k1
+k2
; ++k
) {
247 value_set_si(M
->p
[k
][0], 1);
248 value_set_si(M
->p
[k
][1+k
], 1);
250 for (int k
= 0; k
< d
; ++k
) {
251 value_set_si(M
->p
[k1
+k2
+k
][1+k1
+k2
+k
], -1);
252 zz2value(term
[i
]->n
.power
[j
][k
], M
->p
[k1
+k2
+k
][1+k1
+k2
+d
]);
253 for (int l
= 0; l
< k1
; ++l
)
254 zz2value(term
[i
]->d
.power
[l
][k
], M
->p
[k1
+k2
+k
][1+l
]);
256 for (int k
= 0; k
< d
; ++k
) {
257 value_set_si(M
->p
[k1
+k2
+d
+k
][1+k1
+k2
+k
], -1);
258 zz2value(gf
->term
[i2
]->n
.power
[j2
][k
],
259 M
->p
[k1
+k2
+d
+k
][1+k1
+k2
+d
]);
260 for (int l
= 0; l
< k2
; ++l
)
261 zz2value(gf
->term
[i2
]->d
.power
[l
][k
],
262 M
->p
[k1
+k2
+d
+k
][1+k1
+l
]);
264 Polyhedron
*P
= Constraints2Polyhedron(M
, MaxRays
);
267 gen_fun
*t
= barvinok_series(P
, U
, MaxRays
);
269 ZZ cn
= term
[i
]->n
.coeff
[j
][0] * gf
->term
[i2
]->n
.coeff
[j2
][0];
270 ZZ cd
= term
[i
]->n
.coeff
[j
][1] * gf
->term
[i2
]->n
.coeff
[j2
][1];
283 void gen_fun::add_union(gen_fun
*gf
, unsigned MaxRays
)
289 gen_fun
*hp
= Hadamard_product(gf
, MaxRays
);
295 static void Polyhedron_Shift(Polyhedron
*P
, Vector
*offset
)
299 for (int i
= 0; i
< P
->NbConstraints
; ++i
) {
300 Inner_Product(P
->Constraint
[i
]+1, offset
->p
, P
->Dimension
, &tmp
);
301 value_subtract(P
->Constraint
[i
][1+P
->Dimension
],
302 P
->Constraint
[i
][1+P
->Dimension
], tmp
);
304 for (int i
= 0; i
< P
->NbRays
; ++i
) {
305 if (value_notone_p(P
->Ray
[i
][0]))
307 if (value_zero_p(P
->Ray
[i
][1+P
->Dimension
]))
309 Vector_Combine(P
->Ray
[i
]+1, offset
->p
, P
->Ray
[i
]+1,
310 P
->Ray
[i
][0], P
->Ray
[i
][1+P
->Dimension
], P
->Dimension
);
315 void gen_fun::shift(const vec_ZZ
& offset
)
317 for (int i
= 0; i
< term
.size(); ++i
)
318 for (int j
= 0; j
< term
[i
]->n
.power
.NumRows(); ++j
)
319 term
[i
]->n
.power
[j
] += offset
;
321 Vector
*v
= Vector_Alloc(offset
.length());
322 zz2values(offset
, v
->p
);
323 Polyhedron_Shift(context
, v
);
327 static void print_power(vec_ZZ
& c
, vec_ZZ
& p
,
328 unsigned int nparam
, char **param_name
)
332 for (int i
= 0; i
< p
.length(); ++i
) {
336 if (c
[0] == -1 && c
[1] == 1)
338 else if (c
[0] != 1 || c
[1] != 1) {
341 cout
<< " / " << c
[1];
348 cout
<< param_name
[i
];
354 cout
<< "^(" << p
[i
] << ")";
361 cout
<< " / " << c
[1];
365 void gen_fun::print(unsigned int nparam
, char **param_name
) const
371 for (int i
= 0; i
< term
.size(); ++i
) {
375 for (int j
= 0; j
< term
[i
]->n
.coeff
.NumRows(); ++j
) {
376 if (j
!= 0 && term
[i
]->n
.coeff
[j
][0] > 0)
378 print_power(term
[i
]->n
.coeff
[j
], term
[i
]->n
.power
[j
],
382 for (int j
= 0; j
< term
[i
]->d
.power
.NumRows(); ++j
) {
386 print_power(mone
, term
[i
]->d
.power
[j
],
394 gen_fun::operator evalue
*() const
398 value_init(factor
.d
);
399 value_init(factor
.x
.n
);
400 for (int i
= 0; i
< term
.size(); ++i
) {
401 unsigned nvar
= term
[i
]->d
.power
.NumRows();
402 unsigned nparam
= term
[i
]->d
.power
.NumCols();
403 Matrix
*C
= Matrix_Alloc(nparam
+ nvar
, 1 + nvar
+ nparam
+ 1);
404 mat_ZZ
& d
= term
[i
]->d
.power
;
405 Polyhedron
*U
= context
? context
: Universe_Polyhedron(nparam
);
407 for (int j
= 0; j
< term
[i
]->n
.coeff
.NumRows(); ++j
) {
408 for (int r
= 0; r
< nparam
; ++r
) {
409 value_set_si(C
->p
[r
][0], 0);
410 for (int c
= 0; c
< nvar
; ++c
) {
411 zz2value(d
[c
][r
], C
->p
[r
][1+c
]);
413 Vector_Set(&C
->p
[r
][1+nvar
], 0, nparam
);
414 value_set_si(C
->p
[r
][1+nvar
+r
], -1);
415 zz2value(term
[i
]->n
.power
[j
][r
], C
->p
[r
][1+nvar
+nparam
]);
417 for (int r
= 0; r
< nvar
; ++r
) {
418 value_set_si(C
->p
[nparam
+r
][0], 1);
419 Vector_Set(&C
->p
[nparam
+r
][1], 0, nvar
+ nparam
+ 1);
420 value_set_si(C
->p
[nparam
+r
][1+r
], 1);
422 Polyhedron
*P
= Constraints2Polyhedron(C
, 0);
423 evalue
*E
= barvinok_enumerate_ev(P
, U
, 0);
425 if (EVALUE_IS_ZERO(*E
)) {
430 zz2value(term
[i
]->n
.coeff
[j
][0], factor
.x
.n
);
431 zz2value(term
[i
]->n
.coeff
[j
][1], factor
.d
);
434 Matrix_Print(stdout, P_VALUE_FMT, C);
435 char *test[] = { "A", "B", "C", "D", "E", "F", "G" };
436 print_evalue(stdout, E, test);
450 value_clear(factor
.d
);
451 value_clear(factor
.x
.n
);
455 void gen_fun::coefficient(Value
* params
, Value
* c
) const
457 if (context
&& !in_domain(context
, params
)) {
464 value_init(part
.x
.n
);
467 evalue_set_si(&sum
, 0, 1);
471 for (int i
= 0; i
< term
.size(); ++i
) {
472 unsigned nvar
= term
[i
]->d
.power
.NumRows();
473 unsigned nparam
= term
[i
]->d
.power
.NumCols();
474 Matrix
*C
= Matrix_Alloc(nparam
+ nvar
, 1 + nvar
+ 1);
475 mat_ZZ
& d
= term
[i
]->d
.power
;
477 for (int j
= 0; j
< term
[i
]->n
.coeff
.NumRows(); ++j
) {
478 for (int r
= 0; r
< nparam
; ++r
) {
479 value_set_si(C
->p
[r
][0], 0);
480 for (int c
= 0; c
< nvar
; ++c
) {
481 zz2value(d
[c
][r
], C
->p
[r
][1+c
]);
483 zz2value(term
[i
]->n
.power
[j
][r
], C
->p
[r
][1+nvar
]);
484 value_subtract(C
->p
[r
][1+nvar
], C
->p
[r
][1+nvar
], params
[r
]);
486 for (int r
= 0; r
< nvar
; ++r
) {
487 value_set_si(C
->p
[nparam
+r
][0], 1);
488 Vector_Set(&C
->p
[nparam
+r
][1], 0, nvar
+ 1);
489 value_set_si(C
->p
[nparam
+r
][1+r
], 1);
491 Polyhedron
*P
= Constraints2Polyhedron(C
, 0);
496 barvinok_count(P
, &tmp
, 0);
498 if (value_zero_p(tmp
))
500 zz2value(term
[i
]->n
.coeff
[j
][0], part
.x
.n
);
501 zz2value(term
[i
]->n
.coeff
[j
][1], part
.d
);
502 value_multiply(part
.x
.n
, part
.x
.n
, tmp
);
508 assert(value_one_p(sum
.d
));
509 value_assign(*c
, sum
.x
.n
);
513 value_clear(part
.x
.n
);
515 value_clear(sum
.x
.n
);