[Mlir-commits] [mlir] 14056df - [MLIR] Add support for extracting an integer sample point (if one exists) from an unbounded FlatAffineConstraints.
Arjun P
llvmlistbot at llvm.org
Fri Jan 22 08:58:52 PST 2021
Author: Arjun P
Date: 2021-01-22T22:28:38+05:30
New Revision: 14056dfb4dc7b289fbd12c3bc82f68485bf9377c
URL: https://github.com/llvm/llvm-project/commit/14056dfb4dc7b289fbd12c3bc82f68485bf9377c
DIFF: https://github.com/llvm/llvm-project/commit/14056dfb4dc7b289fbd12c3bc82f68485bf9377c.diff
LOG: [MLIR] Add support for extracting an integer sample point (if one exists) from an unbounded FlatAffineConstraints.
With this, we have complete support for finding integer sample points in FlatAffineConstraints.
Reviewed By: ftynse
Differential Revision: https://reviews.llvm.org/D95047
Added:
Modified:
mlir/include/mlir/Analysis/AffineStructures.h
mlir/include/mlir/Analysis/LinearTransform.h
mlir/include/mlir/Analysis/Presburger/Simplex.h
mlir/lib/Analysis/AffineStructures.cpp
mlir/lib/Analysis/LinearTransform.cpp
mlir/lib/Analysis/Presburger/Simplex.cpp
mlir/unittests/Analysis/AffineStructuresTest.cpp
mlir/unittests/Analysis/LinearTransformTest.cpp
Removed:
################################################################################
diff --git a/mlir/include/mlir/Analysis/AffineStructures.h b/mlir/include/mlir/Analysis/AffineStructures.h
index fa80db7d4b63..fa42db500684 100644
--- a/mlir/include/mlir/Analysis/AffineStructures.h
+++ b/mlir/include/mlir/Analysis/AffineStructures.h
@@ -147,11 +147,8 @@ class FlatAffineConstraints {
/// returns true, no integer solution to the equality constraints can exist.
bool isEmptyByGCDTest() const;
- /// Runs the GCD test heuristic. If it proves inconclusive, falls back to
- /// generalized basis reduction if the set is bounded.
- ///
/// Returns true if the set of constraints is found to have no solution,
- /// false if a solution exists or all tests were inconclusive.
+ /// false if a solution exists. Uses the same algorithm as findIntegerSample.
bool isIntegerEmpty() const;
// Returns a matrix where each row is a vector along which the polytope is
@@ -160,11 +157,12 @@ class FlatAffineConstraints {
// independent. This function should not be called on empty sets.
Matrix getBoundedDirections() const;
- /// Find a sample point satisfying the constraints. This uses a branch and
- /// bound algorithm with generalized basis reduction, which always works if
- /// the set is bounded. This should not be called for unbounded sets.
+ /// Find an integer sample point satisfying the constraints using a
+ /// branch and bound algorithm with generalized basis reduction, with some
+ /// additional processing using Simplex for unbounded sets.
///
- /// Returns such a point if one exists, or an empty Optional otherwise.
+ /// Returns an integer sample point if one exists, or an empty Optional
+ /// otherwise.
Optional<SmallVector<int64_t, 8>> findIntegerSample() const;
/// Returns true if the given point satisfies the constraints, or false
@@ -387,8 +385,9 @@ class FlatAffineConstraints {
/// Changes all symbol identifiers which are loop IVs to dim identifiers.
void convertLoopIVSymbolsToDims();
- /// Sets the specified identifier to a constant and removes it.
- void setAndEliminate(unsigned pos, int64_t constVal);
+ /// Sets the values.size() identifiers starting at pos to the specified values
+ /// and removes them.
+ void setAndEliminate(unsigned pos, ArrayRef<int64_t> values);
/// Tries to fold the specified identifier to a constant using a trivial
/// equality detection; if successful, the constant is substituted for the
diff --git a/mlir/include/mlir/Analysis/LinearTransform.h b/mlir/include/mlir/Analysis/LinearTransform.h
index 0850f5a00609..2f3aaf800ab0 100644
--- a/mlir/include/mlir/Analysis/LinearTransform.h
+++ b/mlir/include/mlir/Analysis/LinearTransform.h
@@ -35,10 +35,15 @@ class LinearTransform {
// Returns a FlatAffineConstraints having a constraint vector vT for every
// constraint vector v in fac, where T is this transform.
- FlatAffineConstraints applyTo(const FlatAffineConstraints &fac);
+ FlatAffineConstraints applyTo(const FlatAffineConstraints &fac) const;
- // Post-multiply the given vector v with this transform, say T, returning vT.
- SmallVector<int64_t, 8> applyTo(ArrayRef<int64_t> v);
+ // The given vector is interpreted as a row vector v. Post-multiply v with
+ // this transform, say T, and return vT.
+ SmallVector<int64_t, 8> postMultiplyRow(ArrayRef<int64_t> rowVec) const;
+
+ // The given vector is interpreted as a column vector v. Pre-multiply v with
+ // this transform, say T, and return Tv.
+ SmallVector<int64_t, 8> preMultiplyColumn(ArrayRef<int64_t> colVec) const;
private:
Matrix matrix;
diff --git a/mlir/include/mlir/Analysis/Presburger/Simplex.h b/mlir/include/mlir/Analysis/Presburger/Simplex.h
index 370035cbc7ba..d64b86d11dec 100644
--- a/mlir/include/mlir/Analysis/Presburger/Simplex.h
+++ b/mlir/include/mlir/Analysis/Presburger/Simplex.h
@@ -218,7 +218,11 @@ class Simplex {
/// tableau A and one in B.
static Simplex makeProduct(const Simplex &a, const Simplex &b);
- /// Returns the current sample point if it is integral. Otherwise, returns an
+ /// Returns a rational sample point. This should not be called when Simplex is
+ /// empty.
+ SmallVector<Fraction, 8> getRationalSample() const;
+
+ /// Returns the current sample point if it is integral. Otherwise, returns
/// None.
Optional<SmallVector<int64_t, 8>> getSamplePointIfIntegral() const;
diff --git a/mlir/lib/Analysis/AffineStructures.cpp b/mlir/lib/Analysis/AffineStructures.cpp
index 12c90fbcfc54..bf4bcab4657f 100644
--- a/mlir/lib/Analysis/AffineStructures.cpp
+++ b/mlir/lib/Analysis/AffineStructures.cpp
@@ -20,6 +20,7 @@
#include "mlir/IR/IntegerSet.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Support/MathExtras.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
@@ -1111,9 +1112,13 @@ void removeConstraintsInvolvingSuffixDims(FlatAffineConstraints &fac,
fac.removeInequality(i - 1);
}
+bool FlatAffineConstraints::isIntegerEmpty() const {
+ return !findIntegerSample().hasValue();
+}
+
/// Let this set be S. If S is bounded then we directly call into the GBR
/// sampling algorithm. Otherwise, there are some unbounded directions, i.e.,
-/// vectors v such that S extends to infininty along v or -v. In this case we
+/// vectors v such that S extends to infinity along v or -v. In this case we
/// use an algorithm described in the integer set library (isl) manual and used
/// by the isl_set_sample function in that library. The algorithm is:
///
@@ -1121,44 +1126,54 @@ void removeConstraintsInvolvingSuffixDims(FlatAffineConstraints &fac,
/// dimensions in which S*T is bounded lie in the linear span of a prefix of the
/// dimensions.
///
-/// 2) Construct a set transformedSet by removing all constraints that involve
-/// the unbounded dimensions and also deleting the unbounded dimensions. Note
-/// that this is a bounded set.
+/// 2) Construct a set B by removing all constraints that involve
+/// the unbounded dimensions and then deleting the unbounded dimensions. Note
+/// that B is a Bounded set.
///
-/// 3) Check if transformedSet is empty using the GBR sampling algorithm.
+/// 3) Try to obtain a sample from B using the GBR sampling
+/// algorithm. If no sample is found, return that S is empty.
///
-/// 4) return S is empty iff transformedSet is empty.
+/// 4) Otherwise, substitute the obtained sample into S*T to obtain a set
+/// C. C is a full-dimensional Cone and always contains a sample.
///
-/// Since T is unimodular, a vector v is a solution to S*T iff T*v is a
-/// solution to S. The following is a sketch of a proof that S*T is empty
-/// iff transformedSet is empty:
+/// 5) Obtain an integer sample from C.
///
-/// If transformedSet is empty, then S*T is certainly empty since transformedSet
-/// was obtained by removing constraints and deleting dimensions from S*T.
+/// 6) Return T*v, where v is the concatenation of the samples from B and C.
///
-/// If transformedSet contains a sample, consider the set C obtained by
-/// substituting the sample for the bounded dimensions of S*T. All the
-/// constraints of S*T that did not involve unbounded dimensions are
-/// satisfied by this substitution.
+/// The following is a sketch of a proof that
+/// a) If the algorithm returns empty, then S is empty.
+/// b) If the algorithm returns a sample, it is a valid sample in S.
///
-/// In step 1, all dimensions in the linear span of the dimensions outside the
-/// prefix are unbounded in S*T. Substituting values for the bounded dimensions
-/// cannot makes these dimensions bounded, and these are the only remaining
-/// dimensions in C, so C is unbounded along every vector. C is hence a
-/// full-dimensional cone and therefore always contains an integer point, which
-/// we can then substitute to get a full solution to S*T.
-bool FlatAffineConstraints::isIntegerEmpty() const {
+/// The algorithm returns empty only if B is empty, in which case S*T is
+/// certainly empty since B was obtained by removing constraints and then
+/// deleting unconstrained dimensions from S*T. Since T is unimodular, a vector
+/// v is in S*T iff T*v is in S. So in this case, since
+/// S*T is empty, S is empty too.
+///
+/// Otherwise, the algorithm substitutes the sample from B into S*T. All the
+/// constraints of S*T that did not involve unbounded dimensions are satisfied
+/// by this substitution. All dimensions in the linear span of the dimensions
+/// outside the prefix are unbounded in S*T (step 1). Substituting values for
+/// the bounded dimensions cannot make these dimensions bounded, and these are
+/// the only remaining dimensions in C, so C is unbounded along every vector (in
+/// the positive or negative direction, or both). C is hence a full-dimensional
+/// cone and therefore always contains an integer point.
+///
+/// Concatenating the samples from B and C gives a sample v in S*T, so the
+/// returned sample T*v is a sample in S.
+Optional<SmallVector<int64_t, 8>>
+FlatAffineConstraints::findIntegerSample() const {
// First, try the GCD test heuristic.
if (isEmptyByGCDTest())
- return true;
+ return {};
Simplex simplex(*this);
if (simplex.isEmpty())
- return true;
+ return {};
// For a bounded set, we directly call into the GBR sampling algorithm.
if (!simplex.isUnbounded())
- return !simplex.findIntegerSample().hasValue();
+ return simplex.findIntegerSample();
// The set is unbounded. We cannot directly use the GBR algorithm.
//
@@ -1172,21 +1187,79 @@ bool FlatAffineConstraints::isIntegerEmpty() const {
// transform to use in step 1 of the algorithm.
std::pair<unsigned, LinearTransform> result =
LinearTransform::makeTransformToColumnEchelon(std::move(m));
- FlatAffineConstraints transformedSet = result.second.applyTo(*this);
+ const LinearTransform &transform = result.second;
+ // 1) Apply T to S to obtain S*T.
+ FlatAffineConstraints transformedSet = transform.applyTo(*this);
+ // 2) Remove the unbounded dimensions and constraints involving them to
+ // obtain a bounded set.
+ FlatAffineConstraints boundedSet = transformedSet;
unsigned numBoundedDims = result.first;
unsigned numUnboundedDims = getNumIds() - numBoundedDims;
- removeConstraintsInvolvingSuffixDims(transformedSet, numUnboundedDims);
+ removeConstraintsInvolvingSuffixDims(boundedSet, numUnboundedDims);
+ boundedSet.removeIdRange(numBoundedDims, boundedSet.getNumIds());
+
+ // 3) Try to obtain a sample from the bounded set.
+ Optional<SmallVector<int64_t, 8>> boundedSample =
+ Simplex(boundedSet).findIntegerSample();
+ if (!boundedSample)
+ return {};
+ assert(boundedSet.containsPoint(*boundedSample) &&
+ "Simplex returned an invalid sample!");
+
+ // 4) Substitute the values of the bounded dimensions into S*T to obtain a
+ // full-dimensional cone, which necessarily contains an integer sample.
+ transformedSet.setAndEliminate(0, *boundedSample);
+ FlatAffineConstraints &cone = transformedSet;
+
+ // 5) Obtain an integer sample from the cone.
+ //
+ // We shrink the cone such that for any rational point in the shrunken cone,
+ // rounding up each of the point's coordinates produces a point that still
+ // lies in the original cone.
+ //
+ // Rounding up a point x adds a number e_i in [0, 1) to each coordinate x_i.
+ // For each inequality sum_i a_i x_i + c >= 0 in the original cone, the
+ // shrunken cone will have the inequality tightened by some amount s, such
+ // that if x satisfies the shrunken cone's tightened inequality, then x + e
+ // satisfies the original inequality, i.e.,
+ //
+ // sum_i a_i x_i + c + s >= 0 implies sum_i a_i (x_i + e_i) + c >= 0
+ //
+ // for any e_i values in [0, 1). In fact, we will handle the slightly more
+ // general case where e_i can be in [0, 1]. For example, consider the
+ // inequality 2x_1 - 3x_2 - 7x_3 - 6 >= 0, and let x = (3, 0, 0). How low
+ // could the LHS go if we added a number in [0, 1] to each coordinate? The LHS
+ // is minimized when we add 1 to the x_i with negative coefficient a_i and
+ // keep the other x_i the same. In the example, we would get x = (3, 1, 1),
+ // changing the value of the LHS by -3 + -7 = -10.
+ //
+ // In general, the value of the LHS can change by at most the sum of the
+ // negative a_i, so we accomodate this by shifting the inequality by this
+ // amount for the shrunken cone.
+ for (unsigned i = 0, e = cone.getNumInequalities(); i < e; ++i) {
+ for (unsigned j = 0; j < cone.numIds; ++j) {
+ int64_t coeff = cone.atIneq(i, j);
+ if (coeff < 0)
+ cone.atIneq(i, cone.numIds) += coeff;
+ }
+ }
- // Remove all the unbounded dimensions.
- transformedSet.removeIdRange(numBoundedDims, transformedSet.getNumIds());
+ // Obtain an integer sample in the cone by rounding up a rational point from
+ // the shrunken cone. Shrinking the cone amounts to shifting its apex
+ // "inwards" without changing its "shape"; the shrunken cone is still a
+ // full-dimensional cone and is hence non-empty.
+ Simplex shrunkenConeSimplex(cone);
+ assert(!shrunkenConeSimplex.isEmpty() && "Shrunken cone cannot be empty!");
+ SmallVector<Fraction, 8> shrunkenConeSample =
+ shrunkenConeSimplex.getRationalSample();
- return !Simplex(transformedSet).findIntegerSample().hasValue();
-}
+ SmallVector<int64_t, 8> coneSample(llvm::map_range(shrunkenConeSample, ceil));
-Optional<SmallVector<int64_t, 8>>
-FlatAffineConstraints::findIntegerSample() const {
- return Simplex(*this).findIntegerSample();
+ // 6) Return transform * concat(boundedSample, coneSample).
+ SmallVector<int64_t, 8> &sample = boundedSample.getValue();
+ sample.append(coneSample.begin(), coneSample.end());
+ return transform.preMultiplyColumn(sample);
}
/// Helper to evaluate an affine expression at a point.
@@ -2204,15 +2277,22 @@ static int findEqualityToConstant(const FlatAffineConstraints &cst,
return -1;
}
-void FlatAffineConstraints::setAndEliminate(unsigned pos, int64_t constVal) {
- assert(pos < getNumIds() && "invalid position");
- for (unsigned r = 0, e = getNumInequalities(); r < e; r++) {
- atIneq(r, getNumCols() - 1) += atIneq(r, pos) * constVal;
- }
- for (unsigned r = 0, e = getNumEqualities(); r < e; r++) {
- atEq(r, getNumCols() - 1) += atEq(r, pos) * constVal;
- }
- removeId(pos);
+void FlatAffineConstraints::setAndEliminate(unsigned pos,
+ ArrayRef<int64_t> values) {
+ if (values.empty())
+ return;
+ assert(pos + values.size() <= getNumIds() &&
+ "invalid position or too many values");
+ // Setting x_j = p in sum_i a_i x_i + c is equivalent to adding p*a_j to the
+ // constant term and removing the id x_j. We do this for all the ids
+ // pos, pos + 1, ... pos + values.size() - 1.
+ for (unsigned r = 0, e = getNumInequalities(); r < e; r++)
+ for (unsigned i = 0, numVals = values.size(); i < numVals; ++i)
+ atIneq(r, getNumCols() - 1) += atIneq(r, pos + i) * values[i];
+ for (unsigned r = 0, e = getNumEqualities(); r < e; r++)
+ for (unsigned i = 0, numVals = values.size(); i < numVals; ++i)
+ atEq(r, getNumCols() - 1) += atEq(r, pos + i) * values[i];
+ removeIdRange(pos, pos + values.size());
}
LogicalResult FlatAffineConstraints::constantFoldId(unsigned pos) {
diff --git a/mlir/lib/Analysis/LinearTransform.cpp b/mlir/lib/Analysis/LinearTransform.cpp
index 7176cb01231f..e8b82c1cad4d 100644
--- a/mlir/lib/Analysis/LinearTransform.cpp
+++ b/mlir/lib/Analysis/LinearTransform.cpp
@@ -111,23 +111,32 @@ LinearTransform::makeTransformToColumnEchelon(Matrix m) {
return {echelonCol, LinearTransform(std::move(resultMatrix))};
}
-SmallVector<int64_t, 8> LinearTransform::applyTo(ArrayRef<int64_t> v) {
- assert(v.size() == matrix.getNumRows() &&
- "vector dimension should be matrix output dimension");
-
- SmallVector<int64_t, 8> result;
- result.reserve(v.size());
- for (unsigned col = 0, e = matrix.getNumColumns(); col < e; ++col) {
- int64_t elem = 0;
+SmallVector<int64_t, 8>
+LinearTransform::postMultiplyRow(ArrayRef<int64_t> rowVec) const {
+ assert(rowVec.size() == matrix.getNumRows() &&
+ "row vector dimension should match transform output dimension");
+
+ SmallVector<int64_t, 8> result(matrix.getNumColumns(), 0);
+ for (unsigned col = 0, e = matrix.getNumColumns(); col < e; ++col)
for (unsigned i = 0, e = matrix.getNumRows(); i < e; ++i)
- elem += v[i] * matrix(i, col);
- result.push_back(elem);
- }
+ result[col] += rowVec[i] * matrix(i, col);
+ return result;
+}
+
+SmallVector<int64_t, 8>
+LinearTransform::preMultiplyColumn(ArrayRef<int64_t> colVec) const {
+ assert(matrix.getNumColumns() == colVec.size() &&
+ "column vector dimension should match transform input dimension");
+
+ SmallVector<int64_t, 8> result(matrix.getNumRows(), 0);
+ for (unsigned row = 0, e = matrix.getNumRows(); row < e; row++)
+ for (unsigned i = 0, e = matrix.getNumColumns(); i < e; i++)
+ result[row] += matrix(row, i) * colVec[i];
return result;
}
FlatAffineConstraints
-LinearTransform::applyTo(const FlatAffineConstraints &fac) {
+LinearTransform::applyTo(const FlatAffineConstraints &fac) const {
FlatAffineConstraints result(fac.getNumDimIds());
for (unsigned i = 0, e = fac.getNumEqualities(); i < e; ++i) {
@@ -135,7 +144,7 @@ LinearTransform::applyTo(const FlatAffineConstraints &fac) {
int64_t c = eq.back();
- SmallVector<int64_t, 8> newEq = applyTo(eq.drop_back());
+ SmallVector<int64_t, 8> newEq = postMultiplyRow(eq.drop_back());
newEq.push_back(c);
result.addEquality(newEq);
}
@@ -145,7 +154,7 @@ LinearTransform::applyTo(const FlatAffineConstraints &fac) {
int64_t c = ineq.back();
- SmallVector<int64_t, 8> newIneq = applyTo(ineq.drop_back());
+ SmallVector<int64_t, 8> newIneq = postMultiplyRow(ineq.drop_back());
newIneq.push_back(c);
result.addInequality(newIneq);
}
diff --git a/mlir/lib/Analysis/Presburger/Simplex.cpp b/mlir/lib/Analysis/Presburger/Simplex.cpp
index 2cfe5929e21d..f3e35f8b2538 100644
--- a/mlir/lib/Analysis/Presburger/Simplex.cpp
+++ b/mlir/lib/Analysis/Presburger/Simplex.cpp
@@ -682,30 +682,42 @@ Simplex Simplex::makeProduct(const Simplex &a, const Simplex &b) {
return result;
}
-Optional<SmallVector<int64_t, 8>> Simplex::getSamplePointIfIntegral() const {
- // The tableau is empty, so no sample point exists.
- if (empty)
- return {};
+SmallVector<Fraction, 8> Simplex::getRationalSample() const {
+ assert(!empty && "This should not be called when Simplex is empty.");
- SmallVector<int64_t, 8> sample;
+ SmallVector<Fraction, 8> sample;
+ sample.reserve(var.size());
// Push the sample value for each variable into the vector.
for (const Unknown &u : var) {
if (u.orientation == Orientation::Column) {
// If the variable is in column position, its sample value is zero.
- sample.push_back(0);
+ sample.emplace_back(0, 1);
} else {
// If the variable is in row position, its sample value is the entry in
// the constant column divided by the entry in the common denominator
- // column. If this is not an integer, then the sample point is not
- // integral so we return None.
- if (tableau(u.pos, 1) % tableau(u.pos, 0) != 0)
- return {};
- sample.push_back(tableau(u.pos, 1) / tableau(u.pos, 0));
+ // column.
+ sample.emplace_back(tableau(u.pos, 1), tableau(u.pos, 0));
}
}
return sample;
}
+Optional<SmallVector<int64_t, 8>> Simplex::getSamplePointIfIntegral() const {
+ // If the tableau is empty, no sample point exists.
+ if (empty)
+ return {};
+ SmallVector<Fraction, 8> rationalSample = getRationalSample();
+ SmallVector<int64_t, 8> integerSample;
+ integerSample.reserve(var.size());
+ for (const Fraction &coord : rationalSample) {
+ // If the sample is non-integral, return None.
+ if (coord.num % coord.den != 0)
+ return {};
+ integerSample.push_back(coord.num / coord.den);
+ }
+ return integerSample;
+}
+
/// Given a simplex for a polytope, construct a new simplex whose variables are
/// identified with a pair of points (x, y) in the original polytope. Supports
/// some operations needed for generalized basis reduction. In what follows,
diff --git a/mlir/unittests/Analysis/AffineStructuresTest.cpp b/mlir/unittests/Analysis/AffineStructuresTest.cpp
index ac11c90ec15b..1f97d0b268ff 100644
--- a/mlir/unittests/Analysis/AffineStructuresTest.cpp
+++ b/mlir/unittests/Analysis/AffineStructuresTest.cpp
@@ -212,45 +212,12 @@ TEST(FlatAffineConstraintsTest, FindSampleTest) {
{0, -2, 99}, // 2y <= 99.
},
{}));
-}
-
-TEST(FlatAffineConstraintsTest, IsIntegerEmptyTest) {
- // 1 <= 5x and 5x <= 4 (no solution).
- EXPECT_TRUE(
- makeFACFromConstraints(1, {{5, -1}, {-5, 4}}, {}).isIntegerEmpty());
- // 1 <= 5x and 5x <= 9 (solution: x = 1).
- EXPECT_FALSE(
- makeFACFromConstraints(1, {{5, -1}, {-5, 9}}, {}).isIntegerEmpty());
-
- // Unbounded sets.
- EXPECT_TRUE(makeFACFromConstraints(3,
- {
- {2, 0, 0, -1}, // 2x >= 1
- {-2, 0, 0, 1}, // 2x <= 1
- {0, 2, 0, -1}, // 2y >= 1
- {0, -2, 0, 1}, // 2y <= 1
- {0, 0, 2, -1}, // 2z >= 1
- },
- {})
- .isIntegerEmpty());
-
- EXPECT_FALSE(makeFACFromConstraints(3,
- {
- {2, 0, 0, -1}, // 2x >= 1
- {-3, 0, 0, 3}, // 3x <= 3
- {0, 0, 5, -6}, // 5z >= 6
- {0, 0, -7, 17}, // 7z <= 17
- {0, 3, 0, -2}, // 3y >= 2
- },
- {})
- .isIntegerEmpty());
-
// 2D cone with apex at (10000, 10000) and
// edges passing through (1/3, 0) and (2/3, 0).
- EXPECT_FALSE(
+ checkSample(
+ true,
makeFACFromConstraints(
- 2, {{300000, -299999, -100000}, {-300000, 299998, 200000}}, {})
- .isIntegerEmpty());
+ 2, {{300000, -299999, -100000}, {-300000, 299998, 200000}}, {}));
// Cartesian product of a tetrahedron and a 2D cone.
// The tetrahedron has vertices at
@@ -276,7 +243,7 @@ TEST(FlatAffineConstraintsTest, IsIntegerEmptyTest) {
// -300000p + 299998q + 200000 >= 0
{0, 0, 0, -300000, 299998, 200000},
},
- {}, TestFunction::Empty);
+ {});
// Cartesian product of same tetrahedron as above and {(p, q) : 1/3 <= p <=
// 2/3}. Since the second set is empty, the whole set is too.
@@ -297,7 +264,7 @@ TEST(FlatAffineConstraintsTest, IsIntegerEmptyTest) {
// 3p <= 2
{0, 0, 0, -3, 0, 2},
},
- {}, TestFunction::Empty);
+ {});
// Cartesian product of same tetrahedron as above and
// {(p, q, r) : 1 <= p <= 2 and p = 3q + 3r}.
@@ -321,8 +288,7 @@ TEST(FlatAffineConstraintsTest, IsIntegerEmptyTest) {
},
{
{0, 0, 0, 1, -3, -3, 0}, // p = 3q + 3r
- },
- TestFunction::Empty);
+ });
// Cartesian product of a tetrahedron and a 2D cone.
// The tetrahedron is empty and has vertices at
@@ -344,7 +310,7 @@ TEST(FlatAffineConstraintsTest, IsIntegerEmptyTest) {
// -300000p + 299998q + 200000 >= 0
{0, 0, 0, -300000, 299998, 200000},
},
- {}, TestFunction::Empty);
+ {});
// Cartesian product of same tetrahedron as above and
// {(p, q) : 1/3 <= p <= 2/3}.
@@ -362,7 +328,47 @@ TEST(FlatAffineConstraintsTest, IsIntegerEmptyTest) {
// 3p <= 2
{0, 0, 0, -3, 0, 2},
},
- {}, TestFunction::Empty);
+ {});
+
+ checkSample(true, makeFACFromConstraints(3,
+ {
+ {2, 0, 0, -1}, // 2x >= 1
+ },
+ {{
+ {1, -1, 0, -1}, // y = x - 1
+ {0, 1, -1, 0}, // z = y
+ }}));
+}
+
+TEST(FlatAffineConstraintsTest, IsIntegerEmptyTest) {
+ // 1 <= 5x and 5x <= 4 (no solution).
+ EXPECT_TRUE(
+ makeFACFromConstraints(1, {{5, -1}, {-5, 4}}, {}).isIntegerEmpty());
+ // 1 <= 5x and 5x <= 9 (solution: x = 1).
+ EXPECT_FALSE(
+ makeFACFromConstraints(1, {{5, -1}, {-5, 9}}, {}).isIntegerEmpty());
+
+ // Unbounded sets.
+ EXPECT_TRUE(makeFACFromConstraints(3,
+ {
+ {0, 2, 0, -1}, // 2y >= 1
+ {0, -2, 0, 1}, // 2y <= 1
+ {0, 0, 2, -1}, // 2z >= 1
+ },
+ {{2, 0, 0, -1}} // 2x = 1
+ )
+ .isIntegerEmpty());
+
+ EXPECT_FALSE(makeFACFromConstraints(3,
+ {
+ {2, 0, 0, -1}, // 2x >= 1
+ {-3, 0, 0, 3}, // 3x <= 3
+ {0, 0, 5, -6}, // 5z >= 6
+ {0, 0, -7, 17}, // 7z <= 17
+ {0, 3, 0, -2}, // 3y >= 2
+ },
+ {})
+ .isIntegerEmpty());
EXPECT_FALSE(makeFACFromConstraints(3,
{
diff --git a/mlir/unittests/Analysis/LinearTransformTest.cpp b/mlir/unittests/Analysis/LinearTransformTest.cpp
index 598c84920d5d..6cdb1ba95bb4 100644
--- a/mlir/unittests/Analysis/LinearTransformTest.cpp
+++ b/mlir/unittests/Analysis/LinearTransformTest.cpp
@@ -22,7 +22,7 @@ void testColumnEchelonForm(const Matrix &m, unsigned expectedRank) {
// In column echelon form, each row's last non-zero value can be at most one
// column to the right of the last non-zero column among the previous rows.
for (unsigned row = 0, nRows = m.getNumRows(); row < nRows; ++row) {
- SmallVector<int64_t, 8> rowVec = transform.applyTo(m.getRow(row));
+ SmallVector<int64_t, 8> rowVec = transform.postMultiplyRow(m.getRow(row));
for (unsigned col = lastAllowedNonZeroCol + 1, nCols = m.getNumColumns();
col < nCols; ++col) {
EXPECT_EQ(rowVec[col], 0);
More information about the Mlir-commits
mailing list