4 #include <barvinok/genfun.h>
5 #include <barvinok/barvinok.h>
9 static int lex_cmp(vec_ZZ
& a
, vec_ZZ
& b
)
11 assert(a
.length() == b
.length());
13 for (int j
= 0; j
< a
.length(); ++j
)
15 return a
[j
] < b
[j
] ? -1 : 1;
19 static int lex_cmp(mat_ZZ
& a
, mat_ZZ
& b
)
21 assert(a
.NumCols() == b
.NumCols());
22 int alen
= a
.NumRows();
23 int blen
= b
.NumRows();
24 int len
= alen
< blen
? alen
: blen
;
26 for (int i
= 0; i
< len
; ++i
) {
27 int s
= lex_cmp(a
[i
], b
[i
]);
34 void gen_fun::add(const ZZ
& cn
, const ZZ
& cd
, const vec_ZZ
& num
,
40 short_rat
* r
= new short_rat
;
41 r
->n
.coeff
.SetDims(1, 2);
43 r
->n
.coeff
[0][0] = cn
/g
;
44 r
->n
.coeff
[0][1] = cd
/g
;
45 r
->n
.power
.SetDims(1, num
.length());
49 /* Make all powers in denominator lexico-positive */
50 for (int i
= 0; i
< r
->d
.power
.NumRows(); ++i
) {
52 for (j
= 0; j
< r
->d
.power
.NumCols(); ++j
)
53 if (r
->d
.power
[i
][j
] != 0)
55 if (r
->d
.power
[i
][j
] < 0) {
56 r
->d
.power
[i
] = -r
->d
.power
[i
];
57 r
->n
.coeff
[0][0] = -r
->n
.coeff
[0][0];
58 r
->n
.power
[0] += r
->d
.power
[i
];
62 /* Order powers in denominator */
63 for (int i
= 0; i
< r
->d
.power
.NumRows(); ++i
) {
65 for (int j
= i
+1; j
< r
->d
.power
.NumRows(); ++j
)
66 if (lex_cmp(r
->d
.power
[j
], r
->d
.power
[m
]) < 0)
69 vec_ZZ tmp
= r
->d
.power
[m
];
70 r
->d
.power
[m
] = r
->d
.power
[i
];
75 for (int i
= 0; i
< term
.size(); ++i
)
76 if (lex_cmp(term
[i
]->d
.power
, r
->d
.power
) == 0) {
77 int len
= term
[i
]->n
.coeff
.NumRows();
79 for (j
= 0; j
< len
; ++j
)
80 if (r
->n
.power
[0] == term
[i
]->n
.power
[j
])
83 ZZ g
= GCD(r
->n
.coeff
[0][1], term
[i
]->n
.coeff
[j
][1]);
84 ZZ n
= term
[i
]->n
.coeff
[j
][0] * (r
->n
.coeff
[0][1] / g
) +
85 (term
[i
]->n
.coeff
[j
][1] / g
) * r
->n
.coeff
[0][0];
86 ZZ d
= term
[i
]->n
.coeff
[j
][1] / g
* r
->n
.coeff
[0][1];
89 term
[i
]->n
.coeff
[j
][0] = n
/g
;
90 term
[i
]->n
.coeff
[j
][1] = d
/g
;
94 term
[i
]->n
.power
[j
] = term
[i
]->n
.power
[len
-1];
95 term
[i
]->n
.coeff
[j
] = term
[i
]->n
.coeff
[len
-1];
97 int dim
= term
[i
]->n
.power
.NumCols();
98 term
[i
]->n
.coeff
.SetDims(len
-1, 2);
99 term
[i
]->n
.power
.SetDims(len
-1, dim
);
102 if (i
!= term
.size()-1)
103 term
[i
] = term
[term
.size()-1];
108 int dim
= term
[i
]->n
.power
.NumCols();
109 term
[i
]->n
.coeff
.SetDims(len
+1, 2);
110 term
[i
]->n
.power
.SetDims(len
+1, dim
);
111 term
[i
]->n
.coeff
[len
] = r
->n
.coeff
[0];
112 term
[i
]->n
.power
[len
] = r
->n
.power
[0];
121 void gen_fun::add(const ZZ
& cn
, const ZZ
& cd
, gen_fun
*gf
)
124 for (int i
= 0; i
< gf
->term
.size(); ++i
) {
125 for (int j
= 0; j
< gf
->term
[i
]->n
.power
.NumRows(); ++j
) {
126 n
= cn
* gf
->term
[i
]->n
.coeff
[j
][0];
127 d
= cd
* gf
->term
[i
]->n
.coeff
[j
][1];
128 add(n
, d
, gf
->term
[i
]->n
.power
[j
], gf
->term
[i
]->d
.power
);
134 * Perform the substitution specified by CP and (map, offset)
136 * CP is a homogeneous matrix that maps a set of "compressed parameters"
137 * to the original set of parameters.
139 * This function is applied to a gen_fun computed with the compressed parameters
140 * and adapts it to refer to the original parameters.
142 * That is, if y are the compressed parameters and x = A y + b are the original
143 * parameters, then we want the coefficient of the monomial t^y in the original
144 * generating function to be the coefficient of the monomial u^x in the resulting
145 * generating function.
146 * The original generating function has the form
148 * a t^m/(1-t^n) = a t^m + a t^{m+n} + a t^{m+2n} + ...
150 * Since each term t^y should correspond to a term u^x, with x = A y + b, we want
152 * a u^{A m + b} + a u^{A (m+n) + b} + a u^{A (m+2n) +b} + ... =
154 * = a u^{A m + b}/(1-u^{A n})
156 * Therefore, we multiply the powers m and n in both numerator and denominator by A
157 * and add b to the power in the numerator.
158 * Since the above powers are stored as row vectors m^T and n^T,
159 * we compute, say, m'^T = m^T A^T to obtain m' = A m.
161 * The pair (map, offset) contains the same information as CP.
162 * map is the transpose of the linear part of CP, while offset is the constant part.
164 void gen_fun::substitute(Matrix
*CP
, const mat_ZZ
& map
, const vec_ZZ
& offset
)
166 Polyhedron
*C
= Polyhedron_Image(context
, CP
, 0);
167 Polyhedron_Free(context
);
169 for (int i
= 0; i
< term
.size(); ++i
) {
170 term
[i
]->d
.power
*= map
;
171 term
[i
]->n
.power
*= map
;
172 for (int j
= 0; j
< term
[i
]->n
.power
.NumRows(); ++j
)
173 term
[i
]->n
.power
[j
] += offset
;
177 gen_fun
*gen_fun::Hadamard_product(gen_fun
*gf
, unsigned MaxRays
)
179 Polyhedron
*C
= DomainIntersection(context
, gf
->context
, MaxRays
);
180 Polyhedron
*U
= Universe_Polyhedron(C
->Dimension
);
181 gen_fun
*sum
= new gen_fun(C
);
182 for (int i
= 0; i
< term
.size(); ++i
) {
183 for (int i2
= 0; i2
< gf
->term
.size(); ++i2
) {
184 int d
= term
[i
]->d
.power
.NumCols();
185 int k1
= term
[i
]->d
.power
.NumRows();
186 int k2
= gf
->term
[i2
]->d
.power
.NumRows();
187 assert(term
[i
]->d
.power
.NumCols() == gf
->term
[i2
]->d
.power
.NumCols());
188 for (int j
= 0; j
< term
[i
]->n
.power
.NumRows(); ++j
) {
189 for (int j2
= 0; j2
< gf
->term
[i2
]->n
.power
.NumRows(); ++j2
) {
190 Matrix
*M
= Matrix_Alloc(k1
+k2
+d
+d
, 1+k1
+k2
+d
+1);
191 for (int k
= 0; k
< k1
+k2
; ++k
) {
192 value_set_si(M
->p
[k
][0], 1);
193 value_set_si(M
->p
[k
][1+k
], 1);
195 for (int k
= 0; k
< d
; ++k
) {
196 value_set_si(M
->p
[k1
+k2
+k
][1+k1
+k2
+k
], -1);
197 zz2value(term
[i
]->n
.power
[j
][k
], M
->p
[k1
+k2
+k
][1+k1
+k2
+d
]);
198 for (int l
= 0; l
< k1
; ++l
)
199 zz2value(term
[i
]->d
.power
[l
][k
], M
->p
[k1
+k2
+k
][1+l
]);
201 for (int k
= 0; k
< d
; ++k
) {
202 value_set_si(M
->p
[k1
+k2
+d
+k
][1+k1
+k2
+k
], -1);
203 zz2value(gf
->term
[i2
]->n
.power
[j2
][k
],
204 M
->p
[k1
+k2
+d
+k
][1+k1
+k2
+d
]);
205 for (int l
= 0; l
< k2
; ++l
)
206 zz2value(gf
->term
[i2
]->d
.power
[l
][k
],
207 M
->p
[k1
+k2
+d
+k
][1+k1
+l
]);
209 Polyhedron
*P
= Constraints2Polyhedron(M
, MaxRays
);
212 gen_fun
*t
= barvinok_series(P
, U
, MaxRays
);
214 ZZ cn
= term
[i
]->n
.coeff
[j
][0] * gf
->term
[i2
]->n
.coeff
[j2
][0];
215 ZZ cd
= term
[i
]->n
.coeff
[j
][1] * gf
->term
[i2
]->n
.coeff
[j2
][1];
228 void gen_fun::add_union(gen_fun
*gf
, unsigned MaxRays
)
234 gen_fun
*hp
= gf
->Hadamard_product(gf
, MaxRays
);
240 static void print_power(vec_ZZ
& c
, vec_ZZ
& p
,
241 unsigned int nparam
, char **param_name
)
245 for (int i
= 0; i
< p
.length(); ++i
) {
249 if (c
[0] == -1 && c
[1] == 1)
251 else if (c
[0] != 1 || c
[1] != 1) {
254 cout
<< " / " << c
[1];
261 cout
<< param_name
[i
];
267 cout
<< "^(" << p
[i
] << ")";
274 cout
<< " / " << c
[1];
278 void gen_fun::print(unsigned int nparam
, char **param_name
) const
284 for (int i
= 0; i
< term
.size(); ++i
) {
288 for (int j
= 0; j
< term
[i
]->n
.coeff
.NumRows(); ++j
) {
289 if (j
!= 0 && term
[i
]->n
.coeff
[j
][0] > 0)
291 print_power(term
[i
]->n
.coeff
[j
], term
[i
]->n
.power
[j
],
295 for (int j
= 0; j
< term
[i
]->d
.power
.NumRows(); ++j
) {
299 print_power(mone
, term
[i
]->d
.power
[j
],
307 gen_fun::operator evalue
*() const
311 value_init(factor
.d
);
312 value_init(factor
.x
.n
);
313 for (int i
= 0; i
< term
.size(); ++i
) {
314 unsigned nvar
= term
[i
]->d
.power
.NumRows();
315 unsigned nparam
= term
[i
]->d
.power
.NumCols();
316 Matrix
*C
= Matrix_Alloc(nparam
+ nvar
, 1 + nvar
+ nparam
+ 1);
317 mat_ZZ
& d
= term
[i
]->d
.power
;
318 Polyhedron
*U
= context
? context
: Universe_Polyhedron(nparam
);
320 for (int j
= 0; j
< term
[i
]->n
.coeff
.NumRows(); ++j
) {
321 for (int r
= 0; r
< nparam
; ++r
) {
322 value_set_si(C
->p
[r
][0], 0);
323 for (int c
= 0; c
< nvar
; ++c
) {
324 zz2value(d
[c
][r
], C
->p
[r
][1+c
]);
326 Vector_Set(&C
->p
[r
][1+nvar
], 0, nparam
);
327 value_set_si(C
->p
[r
][1+nvar
+r
], -1);
328 zz2value(term
[i
]->n
.power
[j
][r
], C
->p
[r
][1+nvar
+nparam
]);
330 for (int r
= 0; r
< nvar
; ++r
) {
331 value_set_si(C
->p
[nparam
+r
][0], 1);
332 Vector_Set(&C
->p
[nparam
+r
][1], 0, nvar
+ nparam
+ 1);
333 value_set_si(C
->p
[nparam
+r
][1+r
], 1);
335 Polyhedron
*P
= Constraints2Polyhedron(C
, 0);
336 evalue
*E
= barvinok_enumerate_ev(P
, U
, 0);
338 if (EVALUE_IS_ZERO(*E
)) {
343 zz2value(term
[i
]->n
.coeff
[j
][0], factor
.x
.n
);
344 zz2value(term
[i
]->n
.coeff
[j
][1], factor
.d
);
347 Matrix_Print(stdout, P_VALUE_FMT, C);
348 char *test[] = { "A", "B", "C", "D", "E", "F", "G" };
349 print_evalue(stdout, E, test);
363 value_clear(factor
.d
);
364 value_clear(factor
.x
.n
);
368 void gen_fun::coefficient(Value
* params
, Value
* c
) const
370 if (context
&& !in_domain(context
, params
)) {
377 value_init(part
.x
.n
);
380 evalue_set_si(&sum
, 0, 1);
384 for (int i
= 0; i
< term
.size(); ++i
) {
385 unsigned nvar
= term
[i
]->d
.power
.NumRows();
386 unsigned nparam
= term
[i
]->d
.power
.NumCols();
387 Matrix
*C
= Matrix_Alloc(nparam
+ nvar
, 1 + nvar
+ 1);
388 mat_ZZ
& d
= term
[i
]->d
.power
;
390 for (int j
= 0; j
< term
[i
]->n
.coeff
.NumRows(); ++j
) {
391 for (int r
= 0; r
< nparam
; ++r
) {
392 value_set_si(C
->p
[r
][0], 0);
393 for (int c
= 0; c
< nvar
; ++c
) {
394 zz2value(d
[c
][r
], C
->p
[r
][1+c
]);
396 zz2value(term
[i
]->n
.power
[j
][r
], C
->p
[r
][1+nvar
]);
397 value_subtract(C
->p
[r
][1+nvar
], C
->p
[r
][1+nvar
], params
[r
]);
399 for (int r
= 0; r
< nvar
; ++r
) {
400 value_set_si(C
->p
[nparam
+r
][0], 1);
401 Vector_Set(&C
->p
[nparam
+r
][1], 0, nvar
+ 1);
402 value_set_si(C
->p
[nparam
+r
][1+r
], 1);
404 Polyhedron
*P
= Constraints2Polyhedron(C
, 0);
409 barvinok_count(P
, &tmp
, 0);
411 if (value_zero_p(tmp
))
413 zz2value(term
[i
]->n
.coeff
[j
][0], part
.x
.n
);
414 zz2value(term
[i
]->n
.coeff
[j
][1], part
.d
);
415 value_multiply(part
.x
.n
, part
.x
.n
, tmp
);
421 assert(value_one_p(sum
.d
));
422 value_assign(*c
, sum
.x
.n
);
426 value_clear(part
.x
.n
);
428 value_clear(sum
.x
.n
);