//===- 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 coeffs, std::vector>> aff) : PresburgerSpace(/*numDomain=*/numVars, /*numRange=*/1, /*numSymbols=*/0, /*numLocals=*/0), coefficients(coeffs), affine(aff) { #ifndef NDEBUG // For each term which involves at least one affine function, for (const std::vector> &term : affine) { if (term.empty()) continue; // the number of elements in each affine function is // one more than the number of symbols. for (const SmallVector &aff : term) { assert(aff.size() == getNumInputs() + 1 && "dimensionality of affine functions does not match number of " "symbols!"); } } #endif // NDEBUG } /// Define a quasipolynomial which is a single constant. QuasiPolynomial::QuasiPolynomial(unsigned numVars, Fraction constant) : PresburgerSpace(/*numDomain=*/numVars, /*numRange=*/1, /*numSymbols=*/0, /*numLocals=*/0), coefficients({constant}), affine({{}}) {} QuasiPolynomial QuasiPolynomial::operator+(const QuasiPolynomial &x) const { assert(getNumInputs() == x.getNumInputs() && "two quasi-polynomials with different numbers of symbols cannot " "be added!"); SmallVector sumCoeffs = coefficients; sumCoeffs.append(x.coefficients); std::vector>> 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 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> product; std::vector>> aff; aff.reserve(affine.size() * x.affine.size()); for (const std::vector> &term : affine) { for (const std::vector> &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 and // integrate affine functions which are constants into the // coefficients. QuasiPolynomial QuasiPolynomial::simplify() { Fraction newCoeff = 0; SmallVector newCoeffs({}); std::vector> newAffineTerm({}); std::vector>> newAffine({}); unsigned numParam = getNumInputs(); 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 &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; // Now, we know the term is nonzero. // We now eliminate the affine functions which are constant // by merging them into the coefficients. newAffineTerm = {}; newCoeff = coefficients[i]; for (ArrayRef term : affine[i]) { bool allCoeffsZero = llvm::all_of( term.slice(0, numParam), [](const Fraction c) { return c == 0; }); if (allCoeffsZero) newCoeff *= term[numParam]; else newAffineTerm.push_back(SmallVector(term)); } newCoeffs.push_back(newCoeff); newAffine.push_back(newAffineTerm); } return QuasiPolynomial(getNumInputs(), newCoeffs, newAffine); } QuasiPolynomial QuasiPolynomial::collectTerms() { SmallVector newCoeffs({}); std::vector>> newAffine({}); for (unsigned i = 0, e = affine.size(); i < e; i++) { bool alreadyPresent = false; for (unsigned j = 0, f = newAffine.size(); j < f; j++) { if (affine[i] == newAffine[j]) { newCoeffs[j] += coefficients[i]; alreadyPresent = true; } } if (alreadyPresent) continue; newCoeffs.push_back(coefficients[i]); newAffine.push_back(affine[i]); } return QuasiPolynomial(getNumInputs(), newCoeffs, newAffine); } Fraction QuasiPolynomial::getConstantTerm() { Fraction constTerm = 0; for (unsigned i = 0, e = coefficients.size(); i < e; ++i) if (affine[i].size() == 0) constTerm += coefficients[i]; return constTerm; }