7
7
// ===----------------------------------------------------------------------===//
8
8
9
9
#include " mlir/Analysis/Presburger/Barvinok.h"
10
+ #include " mlir/Analysis/Presburger/Utils.h"
10
11
#include " llvm/ADT/Sequence.h"
11
12
#include < algorithm>
12
13
@@ -245,3 +246,241 @@ QuasiPolynomial mlir::presburger::detail::getCoefficientInRationalFunction(
245
246
}
246
247
return coefficients[power].simplify ();
247
248
}
249
+
250
+ // / Substitute x_i = t^μ_i in one term of a generating function, returning
251
+ // / a quasipolynomial which represents the exponent of the numerator
252
+ // / of the result, and a vector which represents the exponents of the
253
+ // / denominator of the result.
254
+ // / If the returned value is {num, dens}, it represents the function
255
+ // / t^num / \prod_j (1 - t^dens[j]).
256
+ // / v represents the affine functions whose floors are multiplied by the
257
+ // / generators, and ds represents the list of generators.
258
+ std::pair<QuasiPolynomial, std::vector<Fraction>>
259
+ substituteMuInTerm (unsigned numParams, ParamPoint v, std::vector<Point> ds,
260
+ Point mu) {
261
+ unsigned numDims = mu.size ();
262
+ for (const Point &d : ds)
263
+ assert (d.size () == numDims &&
264
+ " μ has to have the same number of dimensions as the generators!" );
265
+
266
+ // First, the exponent in the numerator becomes
267
+ // - (μ • u_1) * (floor(first col of v))
268
+ // - (μ • u_2) * (floor(second col of v)) - ...
269
+ // - (μ • u_d) * (floor(d'th col of v))
270
+ // So we store the negation of the dot products.
271
+
272
+ // We have d terms, each of whose coefficient is the negative dot product.
273
+ SmallVector<Fraction> coefficients;
274
+ coefficients.reserve (numDims);
275
+ for (const Point &d : ds)
276
+ coefficients.push_back (-dotProduct (mu, d));
277
+
278
+ // Then, the affine function is a single floor expression, given by the
279
+ // corresponding column of v.
280
+ ParamPoint vTranspose = v.transpose ();
281
+ std::vector<std::vector<SmallVector<Fraction>>> affine;
282
+ affine.reserve (numDims);
283
+ for (unsigned j = 0 ; j < numDims; ++j)
284
+ affine.push_back ({SmallVector<Fraction>(vTranspose.getRow (j))});
285
+
286
+ QuasiPolynomial num (numParams, coefficients, affine);
287
+ num = num.simplify ();
288
+
289
+ std::vector<Fraction> dens;
290
+ dens.reserve (ds.size ());
291
+ // Similarly, each term in the denominator has exponent
292
+ // given by the dot product of μ with u_i.
293
+ for (const Point &d : ds) {
294
+ // This term in the denominator is
295
+ // (1 - t^dens.back())
296
+ dens.push_back (dotProduct (d, mu));
297
+ }
298
+
299
+ return {num, dens};
300
+ }
301
+
302
+ // / Normalize all denominator exponents `dens` to their absolute values
303
+ // / by multiplying and dividing by the inverses, in a function of the form
304
+ // / sign * t^num / prod_j (1 - t^dens[j]).
305
+ // / Here, sign = ± 1,
306
+ // / num is a QuasiPolynomial, and
307
+ // / each dens[j] is a Fraction.
308
+ void normalizeDenominatorExponents (int &sign, QuasiPolynomial &num,
309
+ std::vector<Fraction> &dens) {
310
+ // We track the number of exponents that are negative in the
311
+ // denominator, and convert them to their absolute values.
312
+ unsigned numNegExps = 0 ;
313
+ Fraction sumNegExps (0 , 1 );
314
+ for (unsigned j = 0 , e = dens.size (); j < e; ++j) {
315
+ if (dens[j] < 0 ) {
316
+ numNegExps += 1 ;
317
+ sumNegExps += dens[j];
318
+ }
319
+ }
320
+
321
+ // If we have (1 - t^-c) in the denominator, for positive c,
322
+ // multiply and divide by t^c.
323
+ // We convert all negative-exponent terms at once; therefore
324
+ // we multiply and divide by t^sumNegExps.
325
+ // Then we get
326
+ // -(1 - t^c) in the denominator,
327
+ // increase the numerator by c, and
328
+ // flip the sign of the function.
329
+ if (numNegExps % 2 == 1 )
330
+ sign = -sign;
331
+ num = num - QuasiPolynomial (num.getNumInputs (), sumNegExps);
332
+ }
333
+
334
+ // / Compute the binomial coefficients nCi for 0 ≤ i ≤ r,
335
+ // / where n is a QuasiPolynomial.
336
+ std::vector<QuasiPolynomial> getBinomialCoefficients (QuasiPolynomial n,
337
+ unsigned r) {
338
+ unsigned numParams = n.getNumInputs ();
339
+ std::vector<QuasiPolynomial> coefficients;
340
+ coefficients.reserve (r + 1 );
341
+ coefficients.push_back (QuasiPolynomial (numParams, 1 ));
342
+ for (unsigned j = 1 ; j <= r; ++j)
343
+ // We use the recursive formula for binomial coefficients here and below.
344
+ coefficients.push_back (
345
+ (coefficients[j - 1 ] * (n - QuasiPolynomial (numParams, j - 1 )) /
346
+ Fraction (j, 1 ))
347
+ .simplify ());
348
+ return coefficients;
349
+ }
350
+
351
+ // / Compute the binomial coefficients nCi for 0 ≤ i ≤ r,
352
+ // / where n is a QuasiPolynomial.
353
+ std::vector<Fraction> getBinomialCoefficients (Fraction n, Fraction r) {
354
+ std::vector<Fraction> coefficients;
355
+ coefficients.reserve ((int64_t )floor (r));
356
+ coefficients.push_back (1 );
357
+ for (unsigned j = 1 ; j <= r; ++j)
358
+ coefficients.push_back (coefficients[j - 1 ] * (n - (j - 1 )) / (j));
359
+ return coefficients;
360
+ }
361
+
362
+ // / We have a generating function of the form
363
+ // / f_p(x) = \sum_i sign_i * (x^n_i(p)) / (\prod_j (1 - x^d_{ij})
364
+ // /
365
+ // / where sign_i is ±1,
366
+ // / n_i \in Q^p -> Q^d is the sum of the vectors d_{ij}, weighted by the
367
+ // / floors of d affine functions on p parameters.
368
+ // / d_{ij} \in Q^d are vectors.
369
+ // /
370
+ // / We need to find the number of terms of the form x^t in the expansion of
371
+ // / this function.
372
+ // / However, direct substitution (x = (1, ..., 1)) causes the denominator
373
+ // / to become zero.
374
+ // /
375
+ // / We therefore use the following procedure instead:
376
+ // / 1. Substitute x_i = (s+1)^μ_i for some vector μ. This makes the generating
377
+ // / function a function of a scalar s.
378
+ // / 2. Write each term in this function as P(s)/Q(s), where P and Q are
379
+ // / polynomials. P has coefficients as quasipolynomials in d parameters, while
380
+ // / Q has coefficients as scalars.
381
+ // / 3. Find the constant term in the expansion of each term P(s)/Q(s). This is
382
+ // / equivalent to substituting s = 0.
383
+ // /
384
+ // / Verdoolaege, Sven, et al. "Counting integer points in parametric
385
+ // / polytopes using Barvinok's rational functions." Algorithmica 48 (2007):
386
+ // / 37-66.
387
+ QuasiPolynomial
388
+ mlir::presburger::detail::computeNumTerms (const GeneratingFunction &gf) {
389
+ // Step (1) We need to find a μ such that we can substitute x_i =
390
+ // (s+1)^μ_i. After this substitution, the exponent of (s+1) in the
391
+ // denominator is (μ_i • d_{ij}) in each term. Clearly, this cannot become
392
+ // zero. Hence we find a vector μ that is not orthogonal to any of the
393
+ // d_{ij} and substitute x accordingly.
394
+ std::vector<Point> allDenominators;
395
+ for (ArrayRef<Point> den : gf.getDenominators ())
396
+ allDenominators.insert (allDenominators.end (), den.begin (), den.end ());
397
+ Point mu = getNonOrthogonalVector (allDenominators);
398
+
399
+ unsigned numParams = gf.getNumParams ();
400
+ const std::vector<std::vector<Point>> &ds = gf.getDenominators ();
401
+ QuasiPolynomial totalTerm (numParams, 0 );
402
+ for (unsigned i = 0 , e = ds.size (); i < e; ++i) {
403
+ int sign = gf.getSigns ()[i];
404
+
405
+ // Compute the new exponents of (s+1) for the numerator and the
406
+ // denominator after substituting μ.
407
+ auto [numExp, dens] =
408
+ substituteMuInTerm (numParams, gf.getNumerators ()[i], ds[i], mu);
409
+ // Now the numerator is (s+1)^numExp
410
+ // and the denominator is \prod_j (1 - (s+1)^dens[j]).
411
+
412
+ // Step (2) We need to express the terms in the function as quotients of
413
+ // polynomials. Each term is now of the form
414
+ // sign_i * (s+1)^numExp / (\prod_j (1 - (s+1)^dens[j]))
415
+ // For the i'th term, we first normalize the denominator to have only
416
+ // positive exponents. We convert all the dens[j] to their
417
+ // absolute values and change the sign and exponent in the numerator.
418
+ normalizeDenominatorExponents (sign, numExp, dens);
419
+
420
+ // Then, using the formula for geometric series, we replace each (1 -
421
+ // (s+1)^(dens[j])) with
422
+ // (-s)(\sum_{0 ≤ k < dens[j]} (s+1)^k).
423
+ for (unsigned j = 0 , e = dens.size (); j < e; ++j)
424
+ dens[j] = abs (dens[j]) - 1 ;
425
+ // Note that at this point, the semantics of `dens[j]` changes to mean
426
+ // a term (\sum_{0 ≤ k ≤ dens[j]} (s+1)^k). The denominator is, as before,
427
+ // a product of these terms.
428
+
429
+ // Since the -s are taken out, the sign changes if there is an odd number
430
+ // of such terms.
431
+ unsigned r = dens.size ();
432
+ if (dens.size () % 2 == 1 )
433
+ sign = -sign;
434
+
435
+ // Thus the term overall now has the form
436
+ // sign'_i * (s+1)^numExp /
437
+ // (s^r * \prod_j (\sum_{0 ≤ k < dens[j]} (s+1)^k)).
438
+ // This means that
439
+ // the numerator is a polynomial in s, with coefficients as
440
+ // quasipolynomials (given by binomial coefficients), and the denominator
441
+ // is a polynomial in s, with integral coefficients (given by taking the
442
+ // convolution over all j).
443
+
444
+ // Step (3) We need to find the constant term in the expansion of each
445
+ // term. Since each term has s^r as a factor in the denominator, we avoid
446
+ // substituting s = 0 directly; instead, we find the coefficient of s^r in
447
+ // sign'_i * (s+1)^numExp / (\prod_j (\sum_k (s+1)^k)),
448
+ // Letting P(s) = (s+1)^numExp and Q(s) = \prod_j (...),
449
+ // we need to find the coefficient of s^r in P(s)/Q(s),
450
+ // for which we use the `getCoefficientInRationalFunction()` function.
451
+
452
+ // First, we compute the coefficients of P(s), which are binomial
453
+ // coefficients.
454
+ // We only need the first r+1 of these, as higher-order terms do not
455
+ // contribute to the coefficient of s^r.
456
+ std::vector<QuasiPolynomial> numeratorCoefficients =
457
+ getBinomialCoefficients (numExp, r);
458
+
459
+ // Then we compute the coefficients of each individual term in Q(s),
460
+ // which are (dens[i]+1) C (k+1) for 0 ≤ k ≤ dens[i].
461
+ std::vector<std::vector<Fraction>> eachTermDenCoefficients;
462
+ std::vector<Fraction> singleTermDenCoefficients;
463
+ eachTermDenCoefficients.reserve (r);
464
+ for (const Fraction &den : dens) {
465
+ singleTermDenCoefficients = getBinomialCoefficients (den + 1 , den + 1 );
466
+ eachTermDenCoefficients.push_back (
467
+ ArrayRef<Fraction>(singleTermDenCoefficients).slice (1 ));
468
+ }
469
+
470
+ // Now we find the coefficients in Q(s) itself
471
+ // by taking the convolution of the coefficients
472
+ // of all the terms.
473
+ std::vector<Fraction> denominatorCoefficients;
474
+ denominatorCoefficients = eachTermDenCoefficients[0 ];
475
+ for (unsigned j = 1 , e = eachTermDenCoefficients.size (); j < e; ++j)
476
+ denominatorCoefficients = multiplyPolynomials (denominatorCoefficients,
477
+ eachTermDenCoefficients[j]);
478
+
479
+ totalTerm =
480
+ totalTerm + getCoefficientInRationalFunction (r, numeratorCoefficients,
481
+ denominatorCoefficients) *
482
+ QuasiPolynomial (numParams, sign);
483
+ }
484
+
485
+ return totalTerm.simplify ();
486
+ }
0 commit comments