Skip to content

[MLIR][Presburger] Generating functions and quasi-polynomials for Barvinok's algorithm #75702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 44 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b65a078
Definitions for gf and qp classes
Abhinav271828 Dec 16, 2023
6101acb
Comments
Abhinav271828 Dec 16, 2023
5d425bd
Miscellaneous reformatting
Abhinav271828 Dec 16, 2023
883e993
Remove superfluous == operator
Abhinav271828 Dec 16, 2023
2812e75
Use SmallVector::append() instead of insert()
Abhinav271828 Dec 16, 2023
625b1b1
Refactor numParam as private attribute
Abhinav271828 Dec 16, 2023
94b5996
Formatting fixes
Abhinav271828 Dec 17, 2023
f005fdc
Add type instead of auto
Abhinav271828 Dec 17, 2023
34da1b2
Pass numParam
Abhinav271828 Dec 17, 2023
07e5be7
Use range-based loop
Abhinav271828 Dec 17, 2023
f255ad1
Formatting
Abhinav271828 Dec 17, 2023
ef899d9
Use llvm::any_of and llvm::all_of
Abhinav271828 Dec 17, 2023
9bc0cb5
Make data private
Abhinav271828 Dec 18, 2023
d1e6b24
Define getters
Abhinav271828 Dec 18, 2023
c04f6d9
Refactor headers
Abhinav271828 Dec 18, 2023
ebccab2
Define equality of QPs
Abhinav271828 Dec 19, 2023
e89e96b
Formatting
Abhinav271828 Dec 19, 2023
dcc0bf4
Fix arith operators
Abhinav271828 Dec 19, 2023
7b5a469
Formatting
Abhinav271828 Dec 19, 2023
deca816
Formatting
Abhinav271828 Dec 19, 2023
fb8a772
Fix multiplication operator
Abhinav271828 Dec 19, 2023
bb52a35
Arithmetic tests for QP
Abhinav271828 Dec 19, 2023
8803dbf
Test for division
Abhinav271828 Dec 19, 2023
5666b5c
Fix test and add simplify test
Abhinav271828 Dec 19, 2023
47cbb94
Fix GF addition
Abhinav271828 Dec 19, 2023
419c410
Formatting
Abhinav271828 Dec 19, 2023
c46cb86
Fix comments
Abhinav271828 Dec 20, 2023
5bbd0f2
Use const
Abhinav271828 Dec 20, 2023
dee486f
Fix vector definitions
Abhinav271828 Dec 20, 2023
ab1d130
Comments on tests
Abhinav271828 Dec 20, 2023
1a3575e
Fix name of test
Abhinav271828 Dec 20, 2023
712404b
Break down condition
Abhinav271828 Dec 20, 2023
22b8429
Check all terms in assert
Abhinav271828 Dec 20, 2023
e6e05e1
Move getters to header and operators to cpp
Abhinav271828 Dec 20, 2023
94ec54b
Fix QP initialization
Abhinav271828 Dec 22, 2023
59f697a
Return const refs in getters
Abhinav271828 Dec 22, 2023
3eb698b
Fix loop braces
Abhinav271828 Dec 22, 2023
1bada4e
Shift -1 to +1 on unsigned int
Abhinav271828 Dec 22, 2023
1a06ac0
Inherit QP from PresburgerSpace and replace numParam with numVars
Abhinav271828 Dec 22, 2023
933c365
Avoid copying
Abhinav271828 Dec 23, 2023
1281960
Fix formatting of GF
Abhinav271828 Dec 23, 2023
0ea156f
Remove extra includes and use FracMatrix
Abhinav271828 Dec 23, 2023
d921aa4
Fix numerator printing for GF
Abhinav271828 Dec 23, 2023
8b4dd2f
Rename EXPECT_EQ_QUASIPOLYNOMIAL
Abhinav271828 Dec 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions mlir/include/mlir/Analysis/Presburger/QuasiPolynomial.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===- QuasiPolynomial.h - QuasiPolynomial Class ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Definition of the QuasiPolynomial class for Barvinok's algorithm,
// which represents a single-valued function on a set of parameters.
// It is an expression of the form
// f(x) = \sum_i c_i * \prod_j ⌊g_{ij}(x)⌋
// where c_i \in Q and
// g_{ij} : Q^d -> Q are affine functionals over d parameters.
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_ANALYSIS_PRESBURGER_QUASIPOLYNOMIAL_H
#define MLIR_ANALYSIS_PRESBURGER_QUASIPOLYNOMIAL_H

#include "mlir/Analysis/Presburger/Fraction.h"
#include "mlir/Analysis/Presburger/PresburgerSpace.h"

namespace mlir {
namespace presburger {

// A class to describe quasi-polynomials.
// A quasipolynomial consists of a set of terms.
// The ith term is a constant `coefficients[i]`, multiplied
// by the product of a set of affine functions on n parameters.
// Represents functions f : Q^n -> Q of the form
//
// f(x) = \sum_i c_i * \prod_j ⌊g_{ij}(x)⌋
//
// where c_i \in Q and
// g_{ij} : Q^n -> Q are affine functionals.
class QuasiPolynomial : public PresburgerSpace {
public:
QuasiPolynomial(unsigned numVars, SmallVector<Fraction> coeffs = {},
std::vector<std::vector<SmallVector<Fraction>>> aff = {});

// Find the number of inputs (numDomain) to the polynomial.
// numSymbols is set to zero.
unsigned getNumInputs() const {
return getNumDomainVars() + getNumSymbolVars();
}

const SmallVector<Fraction> &getCoefficients() const { return coefficients; }

const std::vector<std::vector<SmallVector<Fraction>>> &getAffine() const {
return affine;
}

// Arithmetic operations.
QuasiPolynomial operator+(const QuasiPolynomial &x) const;
QuasiPolynomial operator-(const QuasiPolynomial &x) const;
QuasiPolynomial operator*(const QuasiPolynomial &x) const;
QuasiPolynomial operator/(const Fraction x) const;

// Removes terms which evaluate to zero from the expression.
QuasiPolynomial simplify();

private:
SmallVector<Fraction> coefficients;
std::vector<std::vector<SmallVector<Fraction>>> affine;
};

} // namespace presburger
} // namespace mlir

#endif // MLIR_ANALYSIS_PRESBURGER_QUASIPOLYNOMIAL_H
1 change: 1 addition & 0 deletions mlir/lib/Analysis/Presburger/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_mlir_library(MLIRPresburger
PresburgerRelation.cpp
PresburgerSpace.cpp
PWMAFunction.cpp
QuasiPolynomial.cpp
Simplex.cpp
SlowMPInt.cpp
Utils.cpp
Expand Down
132 changes: 132 additions & 0 deletions mlir/lib/Analysis/Presburger/GeneratingFunction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//===- GeneratingFunction.h - Generating Functions over Q^d -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Definition of the GeneratingFunction class for Barvinok's algorithm,
// which represents a function over Q^n, parameterized by d parameters.
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_ANALYSIS_PRESBURGER_GENERATINGFUNCTION_H
#define MLIR_ANALYSIS_PRESBURGER_GENERATINGFUNCTION_H

#include "mlir/Analysis/Presburger/Fraction.h"
#include "mlir/Analysis/Presburger/Matrix.h"

namespace mlir {
namespace presburger {

// A parametric point is a vector, each of whose elements
// is an affine function of n parameters. Each row
// in the matrix represents the affine function and
// has n+1 elements.
using ParamPoint = FracMatrix;

// A point is simply a vector.
using Point = SmallVector<Fraction>;

// A class to describe the type of generating function
// used to enumerate the integer points in a polytope.
// Consists of a set of terms, where the ith term has
// * a sign, ±1, stored in `signs[i]`
// * a numerator, of the form x^{n},
// where n, stored in `numerators[i]`,
// is a parametric point.
// * a denominator, of the form (1 - x^{d1})...(1 - x^{dn}),
// where each dj, stored in `denominators[i][j]`,
// is a vector.
//
// Represents functions f_p : Q^n -> Q of the form
//
// f_p(x) = \sum_i s_i * (x^n_i(p)) / (\prod_j (1 - x^d_{ij})
//
// where s_i is ±1,
// n_i \in Q^d -> Q^n is an n-vector of affine functions on d parameters, and
// g_{ij} \in Q^n are vectors.
class GeneratingFunction {
public:
GeneratingFunction(unsigned numParam, SmallVector<int, 8> signs,
std::vector<ParamPoint> nums,
std::vector<std::vector<Point>> dens)
: numParam(numParam), signs(signs), numerators(nums), denominators(dens) {
for (const ParamPoint &term : numerators)
assert(term.getNumColumns() == numParam + 1 &&
"dimensionality of numerator exponents does not match number of "
"parameters!");
}

unsigned getNumParams() { return numParam; }

SmallVector<int> getSigns() { return signs; }

std::vector<ParamPoint> getNumerators() { return numerators; }

std::vector<std::vector<Point>> getDenominators() { return denominators; }

GeneratingFunction operator+(const GeneratingFunction &gf) const {
assert(numParam == gf.getNumParams() &&
"two generating functions with different numbers of parameters "
"cannot be added!");
SmallVector<int> sumSigns = signs;
sumSigns.append(gf.signs);

std::vector<ParamPoint> sumNumerators = numerators;
sumNumerators.insert(sumNumerators.end(), gf.numerators.begin(),
gf.numerators.end());

std::vector<std::vector<Point>> sumDenominators = denominators;
sumDenominators.insert(sumDenominators.end(), gf.denominators.begin(),
gf.denominators.end());
return GeneratingFunction(sumSigns, sumNumerators, sumDenominators);
}

llvm::raw_ostream &print(llvm::raw_ostream &os) const {
for (unsigned i = 0, e = signs.size(); i < e; i++) {
if (i == 0) {
if (signs[i] == -1)
os << "- ";
} else {
if (signs[i] == 1)
os << " + ";
else
os << " - ";
}

os << "x^[";
unsigned r = numerators[i].getNumRows();
for (unsigned j = 0; j < r - 1; j++) {
os << "[";
for (unsigned k = 0, c = numerators[i].getNumColumns(); k < c - 1; k++)
os << numerators[i].at(j, k) << ",";
os << numerators[i].getRow(j).back() << "],";
}
os << "[";
for (unsigned k = 0, c = numerators[i].getNumColumns(); k < c - 1; k++)
os << numerators[i].at(r - 1, k) << ",";
os << numerators[i].getRow(r - 1).back() << "]]/";

for (const Point &den : denominators[i]) {
os << "(x^[";
for (unsigned j = 0, e = den.size(); j < e - 1; j++)
os << den[j] << ",";
os << den.back() << "])";
}
}
return os;
}

private:
unsigned numParam;
SmallVector<int, 8> signs;
std::vector<ParamPoint> numerators;
std::vector<std::vector<Point>> denominators;
};

} // namespace presburger
} // namespace mlir

#endif // MLIR_ANALYSIS_PRESBURGER_GENERATINGFUNCTION_H
113 changes: 113 additions & 0 deletions mlir/lib/Analysis/Presburger/QuasiPolynomial.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//===- QuasiPolynomial.cpp - Quasipolynomial Class --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "mlir/Analysis/Presburger/QuasiPolynomial.h"
#include "mlir/Analysis/Presburger/Fraction.h"
#include "mlir/Analysis/Presburger/PresburgerSpace.h"
#include "mlir/Analysis/Presburger/Utils.h"

using namespace mlir;
using namespace presburger;

QuasiPolynomial::QuasiPolynomial(
unsigned numVars, SmallVector<Fraction> coeffs,
std::vector<std::vector<SmallVector<Fraction>>> aff)
: PresburgerSpace(/*numDomain=*/numVars, /*numRange=*/1, /*numSymbols=*/0,
/*numLocals=*/0),
coefficients(coeffs), affine(aff) {
// For each term which involves at least one affine function,
for (const std::vector<SmallVector<Fraction>> &term : affine) {
if (term.size() == 0)
continue;
// the number of elements in each affine function is
// one more than the number of symbols.
for (const SmallVector<Fraction> &aff : term) {
assert(aff.size() == getNumInputs() + 1 &&
"dimensionality of affine functions does not match number of "
"symbols!");
}
}
}

QuasiPolynomial QuasiPolynomial::operator+(const QuasiPolynomial &x) const {
assert(getNumInputs() == x.getNumInputs() &&
"two quasi-polynomials with different numbers of symbols cannot "
"be added!");
SmallVector<Fraction> sumCoeffs = coefficients;
sumCoeffs.append(x.coefficients);
std::vector<std::vector<SmallVector<Fraction>>> sumAff = affine;
sumAff.insert(sumAff.end(), x.affine.begin(), x.affine.end());
return QuasiPolynomial(getNumInputs(), sumCoeffs, sumAff);
}

QuasiPolynomial QuasiPolynomial::operator-(const QuasiPolynomial &x) const {
assert(getNumInputs() == x.getNumInputs() &&
"two quasi-polynomials with different numbers of symbols cannot "
"be subtracted!");
QuasiPolynomial qp(getNumInputs(), x.coefficients, x.affine);
for (Fraction &coeff : qp.coefficients)
coeff = -coeff;
return *this + qp;
}

QuasiPolynomial QuasiPolynomial::operator*(const QuasiPolynomial &x) const {
assert(getNumInputs() == x.getNumInputs() &&
"two quasi-polynomials with different numbers of "
"symbols cannot be multiplied!");

SmallVector<Fraction> coeffs;
coeffs.reserve(coefficients.size() * x.coefficients.size());
for (const Fraction &coeff : coefficients)
for (const Fraction &xcoeff : x.coefficients)
coeffs.push_back(coeff * xcoeff);

std::vector<SmallVector<Fraction>> product;
std::vector<std::vector<SmallVector<Fraction>>> aff;
aff.reserve(affine.size() * x.affine.size());
for (const std::vector<SmallVector<Fraction>> &term : affine) {
for (const std::vector<SmallVector<Fraction>> &xterm : x.affine) {
product.clear();
product.insert(product.end(), term.begin(), term.end());
product.insert(product.end(), xterm.begin(), xterm.end());
aff.push_back(product);
}
}

return QuasiPolynomial(getNumInputs(), coeffs, aff);
}

QuasiPolynomial QuasiPolynomial::operator/(const Fraction x) const {
assert(x != 0 && "division by zero!");
QuasiPolynomial qp(*this);
for (Fraction &coeff : qp.coefficients)
coeff /= x;
return qp;
}

// Removes terms which evaluate to zero from the expression.
QuasiPolynomial QuasiPolynomial::simplify() {
SmallVector<Fraction> newCoeffs({});
std::vector<std::vector<SmallVector<Fraction>>> newAffine({});
for (unsigned i = 0, e = coefficients.size(); i < e; i++) {
// A term is zero if its coefficient is zero, or
if (coefficients[i] == Fraction(0, 1))
continue;
bool product_is_zero =
// if any of the affine functions in the product
llvm::any_of(affine[i], [](const SmallVector<Fraction> &affine_ij) {
// has all its coefficients as zero.
return llvm::all_of(affine_ij,
[](const Fraction &f) { return f == 0; });
});
if (product_is_zero)
continue;
newCoeffs.push_back(coefficients[i]);
newAffine.push_back(affine[i]);
}
return QuasiPolynomial(getNumInputs(), newCoeffs, newAffine);
}
1 change: 1 addition & 0 deletions mlir/unittests/Analysis/Presburger/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_mlir_unittest(MLIRPresburgerTests
PresburgerRelationTest.cpp
PresburgerSpaceTest.cpp
PWMAFunctionTest.cpp
QuasiPolynomialTest.cpp
SimplexTest.cpp
UtilsTest.cpp
)
Expand Down
Loading