Skip to content

Commit 3702927

Browse files
[mlir][presburger] Preserve relative ordering of symbols and non-symbol vars when setting up SymbolicLexSimplex
The Presburger library's SymbolicLexSimplex class organizes its tableau so that symbols occuply the columns in the range `[3, 3 + num_symbols)`. When creating the SymbolicLexOpt from an IntegerRelation, the API allows the user to specify what variables of the system should be considered as symbols. The constructor then reorders the coefficient when constructing the initial constraint rows. This reordering was also manually at certain users of SymbolicLexOpt, e.g. `IntegerRelation::computeReprWithOnlyDivLocals`. Previously, at least one of the reordering implementations was not stable (in `computeReprWithOnlyDivLocals`). If it considered the the second variable to be a symbol in a 4 variable system: ``` var0 sym1 var2 var3 ``` then the simplex would get arranged as ``` sym1 var3 var2 var0 ``` I discovered tha, in certain cases, this reordering can cause `SymbolicLexOpt::computeSymbolicLexMax` to become extremely inefficient due to a large number of branches through the context tableau as well as an explosion in the size of the coefficients. See the additional test added. So, this change updates the locations where the reordering logic is implemented in order to ensure the partitioning is stable. It is possible that this is just masking a more complicated bug (hard to say for a non-expert on this code). My debugging efforts lead me to believe that there is not a bug in the simplex solver, however. Rather, some pre-processing procedures that rely on heuristics/patterns (e.g. detection of division representations) can fail, which can result in a sub-optimal problem description being handed off to the simplex solver. In any case, a non-stable partitioning of the variables is unexpected and makes it difficult to proactively try to avoid problematic system descriptions by users of the Presburger library.
1 parent 12bdeba commit 3702927

File tree

3 files changed

+32
-24
lines changed

3 files changed

+32
-24
lines changed

mlir/lib/Analysis/Presburger/IntegerRelation.cpp

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -229,25 +229,17 @@ PresburgerRelation IntegerRelation::computeReprWithOnlyDivLocals() const {
229229
// SymbolicLexOpt requires these to form a contiguous range.
230230
//
231231
// Take a copy so we can perform mutations.
232-
IntegerRelation copy = *this;
233232
std::vector<MaybeLocalRepr> reprs(getNumLocalVars());
234-
copy.getLocalReprs(&reprs);
233+
this->getLocalReprs(&reprs);
235234

236-
// Iterate through all the locals. The last `numNonDivLocals` are the locals
237-
// that have been scanned already and do not have division representations.
238235
unsigned numNonDivLocals = 0;
239-
unsigned offset = copy.getVarKindOffset(VarKind::Local);
240-
for (unsigned i = 0, e = copy.getNumLocalVars(); i < e - numNonDivLocals;) {
241-
if (!reprs[i]) {
242-
// Whenever we come across a local that does not have a division
243-
// representation, we swap it to the `numNonDivLocals`-th last position
244-
// and increment `numNonDivLocal`s. `reprs` also needs to be swapped.
245-
copy.swapVar(offset + i, offset + e - numNonDivLocals - 1);
246-
std::swap(reprs[i], reprs[e - numNonDivLocals - 1]);
247-
++numNonDivLocals;
236+
llvm::SmallBitVector isSymbol(getNumVars(), true);
237+
unsigned offset = getVarKindOffset(VarKind::Local);
238+
for (unsigned i = 0, e = getNumLocalVars(); i < e; ++i) {
239+
if (reprs[i])
248240
continue;
249-
}
250-
++i;
241+
isSymbol[offset + i] = false;
242+
++numNonDivLocals;
251243
}
252244

253245
// If there are no non-div locals, we're done.
@@ -266,9 +258,10 @@ PresburgerRelation IntegerRelation::computeReprWithOnlyDivLocals() const {
266258
// and the returned set of assignments to the "symbols" that makes the lexmin
267259
// unbounded.
268260
SymbolicLexOpt lexminResult =
269-
SymbolicLexSimplex(copy, /*symbolOffset*/ 0,
261+
SymbolicLexSimplex(*this,
270262
IntegerPolyhedron(PresburgerSpace::getSetSpace(
271-
/*numDims=*/copy.getNumVars() - numNonDivLocals)))
263+
/*numDims=*/getNumVars() - numNonDivLocals)),
264+
isSymbol)
272265
.computeSymbolicIntegerLexMin();
273266
PresburgerRelation result =
274267
lexminResult.lexopt.getDomain().unionSet(lexminResult.unboundedDomain);

mlir/lib/Analysis/Presburger/Simplex.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,14 @@ SimplexBase::SimplexBase(unsigned nVar, bool mustUseBigM,
6464
const llvm::SmallBitVector &isSymbol)
6565
: SimplexBase(nVar, mustUseBigM) {
6666
assert(isSymbol.size() == nVar && "invalid bitmask!");
67-
// Invariant: nSymbol is the number of symbols that have been marked
68-
// already and these occupy the columns
69-
// [getNumFixedCols(), getNumFixedCols() + nSymbol).
70-
for (unsigned symbolIdx : isSymbol.set_bits()) {
71-
var[symbolIdx].isSymbol = true;
72-
swapColumns(var[symbolIdx].pos, getNumFixedCols() + nSymbol);
73-
++nSymbol;
67+
// Iterate through all the variables. Move symbols to the left and non-symbols
68+
// to the right while preserving relative ordering.
69+
for (unsigned i = 0; i < nVar; ++i) {
70+
if (isSymbol[i]) {
71+
var[i].isSymbol = true;
72+
swapColumns(var[i].pos, getNumFixedCols() + nSymbol);
73+
nSymbol++;
74+
}
7475
}
7576
}
7677

mlir/unittests/Analysis/Presburger/PresburgerSetTest.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,20 @@ TEST(SetTest, computeReprWithOnlyDivLocals) {
855855
PresburgerSet(parseIntegerPolyhedron(
856856
{"(x) : (x - 3*(x floordiv 3) == 0)"})),
857857
/*numToProject=*/2);
858+
859+
testComputeRepr(
860+
parseIntegerPolyhedron("(e, a, b, c)[] : ("
861+
"a >= 0,"
862+
"b >= 0,"
863+
"c >= 0,"
864+
"e >= 0,"
865+
"15 - a >= 0,"
866+
"7 - b >= 0,"
867+
"5 - c >= 0,"
868+
"e - a * 192 - c * 32 - b * 4 >= 0,"
869+
"3 - e + a * 192 + c * 32 + b * 4 >= 0)"),
870+
parsePresburgerSet({"(i) : (i >= 0, i <= 3071)"}),
871+
/*numToProject=*/3);
858872
}
859873

860874
TEST(SetTest, subtractOutputSizeRegression) {

0 commit comments

Comments
 (0)