1 //===- IntegerRelation.cpp - MLIR IntegerRelation Class ---------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // A class to represent an relation over integer tuples. A relation is 10 // represented as a constraint system over a space of tuples of integer valued 11 // varaiables supporting symbolic identifiers and existential quantification. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "mlir/Analysis/Presburger/IntegerRelation.h" 16 #include "mlir/Analysis/Presburger/LinearTransform.h" 17 #include "mlir/Analysis/Presburger/PWMAFunction.h" 18 #include "mlir/Analysis/Presburger/PresburgerRelation.h" 19 #include "mlir/Analysis/Presburger/Simplex.h" 20 #include "mlir/Analysis/Presburger/Utils.h" 21 #include "llvm/ADT/DenseMap.h" 22 #include "llvm/ADT/DenseSet.h" 23 #include "llvm/Support/Debug.h" 24 25 #define DEBUG_TYPE "presburger" 26 27 using namespace mlir; 28 using namespace presburger; 29 30 using llvm::SmallDenseMap; 31 using llvm::SmallDenseSet; 32 33 std::unique_ptr<IntegerRelation> IntegerRelation::clone() const { 34 return std::make_unique<IntegerRelation>(*this); 35 } 36 37 std::unique_ptr<IntegerPolyhedron> IntegerPolyhedron::clone() const { 38 return std::make_unique<IntegerPolyhedron>(*this); 39 } 40 41 void IntegerRelation::append(const IntegerRelation &other) { 42 assert(space.isEqual(other.getSpace()) && "Spaces must be equal."); 43 44 inequalities.reserveRows(inequalities.getNumRows() + 45 other.getNumInequalities()); 46 equalities.reserveRows(equalities.getNumRows() + other.getNumEqualities()); 47 48 for (unsigned r = 0, e = other.getNumInequalities(); r < e; r++) { 49 addInequality(other.getInequality(r)); 50 } 51 for (unsigned r = 0, e = other.getNumEqualities(); r < e; r++) { 52 addEquality(other.getEquality(r)); 53 } 54 } 55 56 IntegerRelation IntegerRelation::intersect(IntegerRelation other) const { 57 IntegerRelation result = *this; 58 result.mergeLocalIds(other); 59 result.append(other); 60 return result; 61 } 62 63 bool IntegerRelation::isEqual(const IntegerRelation &other) const { 64 assert(space.isCompatible(other.getSpace()) && "Spaces must be compatible."); 65 return PresburgerRelation(*this).isEqual(PresburgerRelation(other)); 66 } 67 68 bool IntegerRelation::isSubsetOf(const IntegerRelation &other) const { 69 assert(space.isCompatible(other.getSpace()) && "Spaces must be compatible."); 70 return PresburgerRelation(*this).isSubsetOf(PresburgerRelation(other)); 71 } 72 73 MaybeOptimum<SmallVector<Fraction, 8>> 74 IntegerRelation::findRationalLexMin() const { 75 assert(getNumSymbolIds() == 0 && "Symbols are not supported!"); 76 MaybeOptimum<SmallVector<Fraction, 8>> maybeLexMin = 77 LexSimplex(*this).findRationalLexMin(); 78 79 if (!maybeLexMin.isBounded()) 80 return maybeLexMin; 81 82 // The Simplex returns the lexmin over all the variables including locals. But 83 // locals are not actually part of the space and should not be returned in the 84 // result. Since the locals are placed last in the list of identifiers, they 85 // will be minimized last in the lexmin. So simply truncating out the locals 86 // from the end of the answer gives the desired lexmin over the dimensions. 87 assert(maybeLexMin->size() == getNumIds() && 88 "Incorrect number of vars in lexMin!"); 89 maybeLexMin->resize(getNumDimAndSymbolIds()); 90 return maybeLexMin; 91 } 92 93 MaybeOptimum<SmallVector<int64_t, 8>> 94 IntegerRelation::findIntegerLexMin() const { 95 assert(getNumSymbolIds() == 0 && "Symbols are not supported!"); 96 MaybeOptimum<SmallVector<int64_t, 8>> maybeLexMin = 97 LexSimplex(*this).findIntegerLexMin(); 98 99 if (!maybeLexMin.isBounded()) 100 return maybeLexMin.getKind(); 101 102 // The Simplex returns the lexmin over all the variables including locals. But 103 // locals are not actually part of the space and should not be returned in the 104 // result. Since the locals are placed last in the list of identifiers, they 105 // will be minimized last in the lexmin. So simply truncating out the locals 106 // from the end of the answer gives the desired lexmin over the dimensions. 107 assert(maybeLexMin->size() == getNumIds() && 108 "Incorrect number of vars in lexMin!"); 109 maybeLexMin->resize(getNumDimAndSymbolIds()); 110 return maybeLexMin; 111 } 112 113 static bool rangeIsZero(ArrayRef<int64_t> range) { 114 return llvm::all_of(range, [](int64_t x) { return x == 0; }); 115 } 116 117 void removeConstraintsInvolvingIdRange(IntegerRelation &poly, unsigned begin, 118 unsigned count) { 119 // We loop until i > 0 and index into i - 1 to avoid sign issues. 120 // 121 // We iterate backwards so that whether we remove constraint i - 1 or not, the 122 // next constraint to be tested is always i - 2. 123 for (unsigned i = poly.getNumEqualities(); i > 0; i--) 124 if (!rangeIsZero(poly.getEquality(i - 1).slice(begin, count))) 125 poly.removeEquality(i - 1); 126 for (unsigned i = poly.getNumInequalities(); i > 0; i--) 127 if (!rangeIsZero(poly.getInequality(i - 1).slice(begin, count))) 128 poly.removeInequality(i - 1); 129 } 130 131 IntegerRelation::CountsSnapshot IntegerRelation::getCounts() const { 132 return {getSpace(), getNumInequalities(), getNumEqualities()}; 133 } 134 135 void IntegerRelation::truncateIdKind(IdKind kind, unsigned num) { 136 unsigned curNum = getNumIdKind(kind); 137 assert(num <= curNum && "Can't truncate to more ids!"); 138 removeIdRange(kind, num, curNum); 139 } 140 141 void IntegerRelation::truncateIdKind(IdKind kind, 142 const CountsSnapshot &counts) { 143 truncateIdKind(kind, counts.getSpace().getNumIdKind(kind)); 144 } 145 146 void IntegerRelation::truncate(const CountsSnapshot &counts) { 147 truncateIdKind(IdKind::Domain, counts); 148 truncateIdKind(IdKind::Range, counts); 149 truncateIdKind(IdKind::Symbol, counts); 150 truncateIdKind(IdKind::Local, counts); 151 removeInequalityRange(counts.getNumIneqs(), getNumInequalities()); 152 removeEqualityRange(counts.getNumEqs(), getNumEqualities()); 153 } 154 155 SymbolicLexMin IntegerPolyhedron::findSymbolicIntegerLexMin() const { 156 // Compute the symbolic lexmin of the dims and locals, with the symbols being 157 // the actual symbols of this set. 158 SymbolicLexMin result = 159 SymbolicLexSimplex(*this, IntegerPolyhedron(PresburgerSpace::getSetSpace( 160 /*numDims=*/getNumSymbolIds()))) 161 .computeSymbolicIntegerLexMin(); 162 163 // We want to return only the lexmin over the dims, so strip the locals from 164 // the computed lexmin. 165 result.lexmin.truncateOutput(result.lexmin.getNumOutputs() - 166 getNumLocalIds()); 167 return result; 168 } 169 170 unsigned IntegerRelation::insertId(IdKind kind, unsigned pos, unsigned num) { 171 assert(pos <= getNumIdKind(kind)); 172 173 unsigned insertPos = space.insertId(kind, pos, num); 174 inequalities.insertColumns(insertPos, num); 175 equalities.insertColumns(insertPos, num); 176 return insertPos; 177 } 178 179 unsigned IntegerRelation::appendId(IdKind kind, unsigned num) { 180 unsigned pos = getNumIdKind(kind); 181 return insertId(kind, pos, num); 182 } 183 184 void IntegerRelation::addEquality(ArrayRef<int64_t> eq) { 185 assert(eq.size() == getNumCols()); 186 unsigned row = equalities.appendExtraRow(); 187 for (unsigned i = 0, e = eq.size(); i < e; ++i) 188 equalities(row, i) = eq[i]; 189 } 190 191 void IntegerRelation::addInequality(ArrayRef<int64_t> inEq) { 192 assert(inEq.size() == getNumCols()); 193 unsigned row = inequalities.appendExtraRow(); 194 for (unsigned i = 0, e = inEq.size(); i < e; ++i) 195 inequalities(row, i) = inEq[i]; 196 } 197 198 void IntegerRelation::removeId(IdKind kind, unsigned pos) { 199 removeIdRange(kind, pos, pos + 1); 200 } 201 202 void IntegerRelation::removeId(unsigned pos) { removeIdRange(pos, pos + 1); } 203 204 void IntegerRelation::removeIdRange(IdKind kind, unsigned idStart, 205 unsigned idLimit) { 206 assert(idLimit <= getNumIdKind(kind)); 207 208 if (idStart >= idLimit) 209 return; 210 211 // Remove eliminated identifiers from the constraints. 212 unsigned offset = getIdKindOffset(kind); 213 equalities.removeColumns(offset + idStart, idLimit - idStart); 214 inequalities.removeColumns(offset + idStart, idLimit - idStart); 215 216 // Remove eliminated identifiers from the space. 217 space.removeIdRange(kind, idStart, idLimit); 218 } 219 220 void IntegerRelation::removeIdRange(unsigned idStart, unsigned idLimit) { 221 assert(idLimit <= getNumIds()); 222 223 if (idStart >= idLimit) 224 return; 225 226 // Helper function to remove ids of the specified kind in the given range 227 // [start, limit), The range is absolute (i.e. it is not relative to the kind 228 // of identifier). Also updates `limit` to reflect the deleted identifiers. 229 auto removeIdKindInRange = [this](IdKind kind, unsigned &start, 230 unsigned &limit) { 231 if (start >= limit) 232 return; 233 234 unsigned offset = getIdKindOffset(kind); 235 unsigned num = getNumIdKind(kind); 236 237 // Get `start`, `limit` relative to the specified kind. 238 unsigned relativeStart = 239 start <= offset ? 0 : std::min(num, start - offset); 240 unsigned relativeLimit = 241 limit <= offset ? 0 : std::min(num, limit - offset); 242 243 // Remove ids of the specified kind in the relative range. 244 removeIdRange(kind, relativeStart, relativeLimit); 245 246 // Update `limit` to reflect deleted identifiers. 247 // `start` does not need to be updated because any identifiers that are 248 // deleted are after position `start`. 249 limit -= relativeLimit - relativeStart; 250 }; 251 252 removeIdKindInRange(IdKind::Domain, idStart, idLimit); 253 removeIdKindInRange(IdKind::Range, idStart, idLimit); 254 removeIdKindInRange(IdKind::Symbol, idStart, idLimit); 255 removeIdKindInRange(IdKind::Local, idStart, idLimit); 256 } 257 258 void IntegerRelation::removeEquality(unsigned pos) { 259 equalities.removeRow(pos); 260 } 261 262 void IntegerRelation::removeInequality(unsigned pos) { 263 inequalities.removeRow(pos); 264 } 265 266 void IntegerRelation::removeEqualityRange(unsigned start, unsigned end) { 267 if (start >= end) 268 return; 269 equalities.removeRows(start, end - start); 270 } 271 272 void IntegerRelation::removeInequalityRange(unsigned start, unsigned end) { 273 if (start >= end) 274 return; 275 inequalities.removeRows(start, end - start); 276 } 277 278 void IntegerRelation::swapId(unsigned posA, unsigned posB) { 279 assert(posA < getNumIds() && "invalid position A"); 280 assert(posB < getNumIds() && "invalid position B"); 281 282 if (posA == posB) 283 return; 284 285 inequalities.swapColumns(posA, posB); 286 equalities.swapColumns(posA, posB); 287 } 288 289 void IntegerRelation::clearConstraints() { 290 equalities.resizeVertically(0); 291 inequalities.resizeVertically(0); 292 } 293 294 /// Gather all lower and upper bounds of the identifier at `pos`, and 295 /// optionally any equalities on it. In addition, the bounds are to be 296 /// independent of identifiers in position range [`offset`, `offset` + `num`). 297 void IntegerRelation::getLowerAndUpperBoundIndices( 298 unsigned pos, SmallVectorImpl<unsigned> *lbIndices, 299 SmallVectorImpl<unsigned> *ubIndices, SmallVectorImpl<unsigned> *eqIndices, 300 unsigned offset, unsigned num) const { 301 assert(pos < getNumIds() && "invalid position"); 302 assert(offset + num < getNumCols() && "invalid range"); 303 304 // Checks for a constraint that has a non-zero coeff for the identifiers in 305 // the position range [offset, offset + num) while ignoring `pos`. 306 auto containsConstraintDependentOnRange = [&](unsigned r, bool isEq) { 307 unsigned c, f; 308 auto cst = isEq ? getEquality(r) : getInequality(r); 309 for (c = offset, f = offset + num; c < f; ++c) { 310 if (c == pos) 311 continue; 312 if (cst[c] != 0) 313 break; 314 } 315 return c < f; 316 }; 317 318 // Gather all lower bounds and upper bounds of the variable. Since the 319 // canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower 320 // bound for x_i if c_i >= 1, and an upper bound if c_i <= -1. 321 for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 322 // The bounds are to be independent of [offset, offset + num) columns. 323 if (containsConstraintDependentOnRange(r, /*isEq=*/false)) 324 continue; 325 if (atIneq(r, pos) >= 1) { 326 // Lower bound. 327 lbIndices->push_back(r); 328 } else if (atIneq(r, pos) <= -1) { 329 // Upper bound. 330 ubIndices->push_back(r); 331 } 332 } 333 334 // An equality is both a lower and upper bound. Record any equalities 335 // involving the pos^th identifier. 336 if (!eqIndices) 337 return; 338 339 for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 340 if (atEq(r, pos) == 0) 341 continue; 342 if (containsConstraintDependentOnRange(r, /*isEq=*/true)) 343 continue; 344 eqIndices->push_back(r); 345 } 346 } 347 348 bool IntegerRelation::hasConsistentState() const { 349 if (!inequalities.hasConsistentState()) 350 return false; 351 if (!equalities.hasConsistentState()) 352 return false; 353 return true; 354 } 355 356 void IntegerRelation::setAndEliminate(unsigned pos, ArrayRef<int64_t> values) { 357 if (values.empty()) 358 return; 359 assert(pos + values.size() <= getNumIds() && 360 "invalid position or too many values"); 361 // Setting x_j = p in sum_i a_i x_i + c is equivalent to adding p*a_j to the 362 // constant term and removing the id x_j. We do this for all the ids 363 // pos, pos + 1, ... pos + values.size() - 1. 364 unsigned constantColPos = getNumCols() - 1; 365 for (unsigned i = 0, numVals = values.size(); i < numVals; ++i) 366 inequalities.addToColumn(i + pos, constantColPos, values[i]); 367 for (unsigned i = 0, numVals = values.size(); i < numVals; ++i) 368 equalities.addToColumn(i + pos, constantColPos, values[i]); 369 removeIdRange(pos, pos + values.size()); 370 } 371 372 void IntegerRelation::clearAndCopyFrom(const IntegerRelation &other) { 373 *this = other; 374 } 375 376 // Searches for a constraint with a non-zero coefficient at `colIdx` in 377 // equality (isEq=true) or inequality (isEq=false) constraints. 378 // Returns true and sets row found in search in `rowIdx`, false otherwise. 379 bool IntegerRelation::findConstraintWithNonZeroAt(unsigned colIdx, bool isEq, 380 unsigned *rowIdx) const { 381 assert(colIdx < getNumCols() && "position out of bounds"); 382 auto at = [&](unsigned rowIdx) -> int64_t { 383 return isEq ? atEq(rowIdx, colIdx) : atIneq(rowIdx, colIdx); 384 }; 385 unsigned e = isEq ? getNumEqualities() : getNumInequalities(); 386 for (*rowIdx = 0; *rowIdx < e; ++(*rowIdx)) { 387 if (at(*rowIdx) != 0) { 388 return true; 389 } 390 } 391 return false; 392 } 393 394 void IntegerRelation::normalizeConstraintsByGCD() { 395 for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) 396 equalities.normalizeRow(i); 397 for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) 398 inequalities.normalizeRow(i); 399 } 400 401 bool IntegerRelation::hasInvalidConstraint() const { 402 assert(hasConsistentState()); 403 auto check = [&](bool isEq) -> bool { 404 unsigned numCols = getNumCols(); 405 unsigned numRows = isEq ? getNumEqualities() : getNumInequalities(); 406 for (unsigned i = 0, e = numRows; i < e; ++i) { 407 unsigned j; 408 for (j = 0; j < numCols - 1; ++j) { 409 int64_t v = isEq ? atEq(i, j) : atIneq(i, j); 410 // Skip rows with non-zero variable coefficients. 411 if (v != 0) 412 break; 413 } 414 if (j < numCols - 1) { 415 continue; 416 } 417 // Check validity of constant term at 'numCols - 1' w.r.t 'isEq'. 418 // Example invalid constraints include: '1 == 0' or '-1 >= 0' 419 int64_t v = isEq ? atEq(i, numCols - 1) : atIneq(i, numCols - 1); 420 if ((isEq && v != 0) || (!isEq && v < 0)) { 421 return true; 422 } 423 } 424 return false; 425 }; 426 if (check(/*isEq=*/true)) 427 return true; 428 return check(/*isEq=*/false); 429 } 430 431 /// Eliminate identifier from constraint at `rowIdx` based on coefficient at 432 /// pivotRow, pivotCol. Columns in range [elimColStart, pivotCol) will not be 433 /// updated as they have already been eliminated. 434 static void eliminateFromConstraint(IntegerRelation *constraints, 435 unsigned rowIdx, unsigned pivotRow, 436 unsigned pivotCol, unsigned elimColStart, 437 bool isEq) { 438 // Skip if equality 'rowIdx' if same as 'pivotRow'. 439 if (isEq && rowIdx == pivotRow) 440 return; 441 auto at = [&](unsigned i, unsigned j) -> int64_t { 442 return isEq ? constraints->atEq(i, j) : constraints->atIneq(i, j); 443 }; 444 int64_t leadCoeff = at(rowIdx, pivotCol); 445 // Skip if leading coefficient at 'rowIdx' is already zero. 446 if (leadCoeff == 0) 447 return; 448 int64_t pivotCoeff = constraints->atEq(pivotRow, pivotCol); 449 int64_t sign = (leadCoeff * pivotCoeff > 0) ? -1 : 1; 450 int64_t lcm = mlir::lcm(pivotCoeff, leadCoeff); 451 int64_t pivotMultiplier = sign * (lcm / std::abs(pivotCoeff)); 452 int64_t rowMultiplier = lcm / std::abs(leadCoeff); 453 454 unsigned numCols = constraints->getNumCols(); 455 for (unsigned j = 0; j < numCols; ++j) { 456 // Skip updating column 'j' if it was just eliminated. 457 if (j >= elimColStart && j < pivotCol) 458 continue; 459 int64_t v = pivotMultiplier * constraints->atEq(pivotRow, j) + 460 rowMultiplier * at(rowIdx, j); 461 isEq ? constraints->atEq(rowIdx, j) = v 462 : constraints->atIneq(rowIdx, j) = v; 463 } 464 } 465 466 /// Returns the position of the identifier that has the minimum <number of lower 467 /// bounds> times <number of upper bounds> from the specified range of 468 /// identifiers [start, end). It is often best to eliminate in the increasing 469 /// order of these counts when doing Fourier-Motzkin elimination since FM adds 470 /// that many new constraints. 471 static unsigned getBestIdToEliminate(const IntegerRelation &cst, unsigned start, 472 unsigned end) { 473 assert(start < cst.getNumIds() && end < cst.getNumIds() + 1); 474 475 auto getProductOfNumLowerUpperBounds = [&](unsigned pos) { 476 unsigned numLb = 0; 477 unsigned numUb = 0; 478 for (unsigned r = 0, e = cst.getNumInequalities(); r < e; r++) { 479 if (cst.atIneq(r, pos) > 0) { 480 ++numLb; 481 } else if (cst.atIneq(r, pos) < 0) { 482 ++numUb; 483 } 484 } 485 return numLb * numUb; 486 }; 487 488 unsigned minLoc = start; 489 unsigned min = getProductOfNumLowerUpperBounds(start); 490 for (unsigned c = start + 1; c < end; c++) { 491 unsigned numLbUbProduct = getProductOfNumLowerUpperBounds(c); 492 if (numLbUbProduct < min) { 493 min = numLbUbProduct; 494 minLoc = c; 495 } 496 } 497 return minLoc; 498 } 499 500 // Checks for emptiness of the set by eliminating identifiers successively and 501 // using the GCD test (on all equality constraints) and checking for trivially 502 // invalid constraints. Returns 'true' if the constraint system is found to be 503 // empty; false otherwise. 504 bool IntegerRelation::isEmpty() const { 505 if (isEmptyByGCDTest() || hasInvalidConstraint()) 506 return true; 507 508 IntegerRelation tmpCst(*this); 509 510 // First, eliminate as many local variables as possible using equalities. 511 tmpCst.removeRedundantLocalVars(); 512 if (tmpCst.isEmptyByGCDTest() || tmpCst.hasInvalidConstraint()) 513 return true; 514 515 // Eliminate as many identifiers as possible using Gaussian elimination. 516 unsigned currentPos = 0; 517 while (currentPos < tmpCst.getNumIds()) { 518 tmpCst.gaussianEliminateIds(currentPos, tmpCst.getNumIds()); 519 ++currentPos; 520 // We check emptiness through trivial checks after eliminating each ID to 521 // detect emptiness early. Since the checks isEmptyByGCDTest() and 522 // hasInvalidConstraint() are linear time and single sweep on the constraint 523 // buffer, this appears reasonable - but can optimize in the future. 524 if (tmpCst.hasInvalidConstraint() || tmpCst.isEmptyByGCDTest()) 525 return true; 526 } 527 528 // Eliminate the remaining using FM. 529 for (unsigned i = 0, e = tmpCst.getNumIds(); i < e; i++) { 530 tmpCst.fourierMotzkinEliminate( 531 getBestIdToEliminate(tmpCst, 0, tmpCst.getNumIds())); 532 // Check for a constraint explosion. This rarely happens in practice, but 533 // this check exists as a safeguard against improperly constructed 534 // constraint systems or artificially created arbitrarily complex systems 535 // that aren't the intended use case for IntegerRelation. This is 536 // needed since FM has a worst case exponential complexity in theory. 537 if (tmpCst.getNumConstraints() >= kExplosionFactor * getNumIds()) { 538 LLVM_DEBUG(llvm::dbgs() << "FM constraint explosion detected\n"); 539 return false; 540 } 541 542 // FM wouldn't have modified the equalities in any way. So no need to again 543 // run GCD test. Check for trivial invalid constraints. 544 if (tmpCst.hasInvalidConstraint()) 545 return true; 546 } 547 return false; 548 } 549 550 // Runs the GCD test on all equality constraints. Returns 'true' if this test 551 // fails on any equality. Returns 'false' otherwise. 552 // This test can be used to disprove the existence of a solution. If it returns 553 // true, no integer solution to the equality constraints can exist. 554 // 555 // GCD test definition: 556 // 557 // The equality constraint: 558 // 559 // c_1*x_1 + c_2*x_2 + ... + c_n*x_n = c_0 560 // 561 // has an integer solution iff: 562 // 563 // GCD of c_1, c_2, ..., c_n divides c_0. 564 // 565 bool IntegerRelation::isEmptyByGCDTest() const { 566 assert(hasConsistentState()); 567 unsigned numCols = getNumCols(); 568 for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 569 uint64_t gcd = std::abs(atEq(i, 0)); 570 for (unsigned j = 1; j < numCols - 1; ++j) { 571 gcd = llvm::GreatestCommonDivisor64(gcd, std::abs(atEq(i, j))); 572 } 573 int64_t v = std::abs(atEq(i, numCols - 1)); 574 if (gcd > 0 && (v % gcd != 0)) { 575 return true; 576 } 577 } 578 return false; 579 } 580 581 // Returns a matrix where each row is a vector along which the polytope is 582 // bounded. The span of the returned vectors is guaranteed to contain all 583 // such vectors. The returned vectors are NOT guaranteed to be linearly 584 // independent. This function should not be called on empty sets. 585 // 586 // It is sufficient to check the perpendiculars of the constraints, as the set 587 // of perpendiculars which are bounded must span all bounded directions. 588 Matrix IntegerRelation::getBoundedDirections() const { 589 // Note that it is necessary to add the equalities too (which the constructor 590 // does) even though we don't need to check if they are bounded; whether an 591 // inequality is bounded or not depends on what other constraints, including 592 // equalities, are present. 593 Simplex simplex(*this); 594 595 assert(!simplex.isEmpty() && "It is not meaningful to ask whether a " 596 "direction is bounded in an empty set."); 597 598 SmallVector<unsigned, 8> boundedIneqs; 599 // The constructor adds the inequalities to the simplex first, so this 600 // processes all the inequalities. 601 for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 602 if (simplex.isBoundedAlongConstraint(i)) 603 boundedIneqs.push_back(i); 604 } 605 606 // The direction vector is given by the coefficients and does not include the 607 // constant term, so the matrix has one fewer column. 608 unsigned dirsNumCols = getNumCols() - 1; 609 Matrix dirs(boundedIneqs.size() + getNumEqualities(), dirsNumCols); 610 611 // Copy the bounded inequalities. 612 unsigned row = 0; 613 for (unsigned i : boundedIneqs) { 614 for (unsigned col = 0; col < dirsNumCols; ++col) 615 dirs(row, col) = atIneq(i, col); 616 ++row; 617 } 618 619 // Copy the equalities. All the equalities' perpendiculars are bounded. 620 for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 621 for (unsigned col = 0; col < dirsNumCols; ++col) 622 dirs(row, col) = atEq(i, col); 623 ++row; 624 } 625 626 return dirs; 627 } 628 629 bool IntegerRelation::isIntegerEmpty() const { 630 return !findIntegerSample().hasValue(); 631 } 632 633 /// Let this set be S. If S is bounded then we directly call into the GBR 634 /// sampling algorithm. Otherwise, there are some unbounded directions, i.e., 635 /// vectors v such that S extends to infinity along v or -v. In this case we 636 /// use an algorithm described in the integer set library (isl) manual and used 637 /// by the isl_set_sample function in that library. The algorithm is: 638 /// 639 /// 1) Apply a unimodular transform T to S to obtain S*T, such that all 640 /// dimensions in which S*T is bounded lie in the linear span of a prefix of the 641 /// dimensions. 642 /// 643 /// 2) Construct a set B by removing all constraints that involve 644 /// the unbounded dimensions and then deleting the unbounded dimensions. Note 645 /// that B is a Bounded set. 646 /// 647 /// 3) Try to obtain a sample from B using the GBR sampling 648 /// algorithm. If no sample is found, return that S is empty. 649 /// 650 /// 4) Otherwise, substitute the obtained sample into S*T to obtain a set 651 /// C. C is a full-dimensional Cone and always contains a sample. 652 /// 653 /// 5) Obtain an integer sample from C. 654 /// 655 /// 6) Return T*v, where v is the concatenation of the samples from B and C. 656 /// 657 /// The following is a sketch of a proof that 658 /// a) If the algorithm returns empty, then S is empty. 659 /// b) If the algorithm returns a sample, it is a valid sample in S. 660 /// 661 /// The algorithm returns empty only if B is empty, in which case S*T is 662 /// certainly empty since B was obtained by removing constraints and then 663 /// deleting unconstrained dimensions from S*T. Since T is unimodular, a vector 664 /// v is in S*T iff T*v is in S. So in this case, since 665 /// S*T is empty, S is empty too. 666 /// 667 /// Otherwise, the algorithm substitutes the sample from B into S*T. All the 668 /// constraints of S*T that did not involve unbounded dimensions are satisfied 669 /// by this substitution. All dimensions in the linear span of the dimensions 670 /// outside the prefix are unbounded in S*T (step 1). Substituting values for 671 /// the bounded dimensions cannot make these dimensions bounded, and these are 672 /// the only remaining dimensions in C, so C is unbounded along every vector (in 673 /// the positive or negative direction, or both). C is hence a full-dimensional 674 /// cone and therefore always contains an integer point. 675 /// 676 /// Concatenating the samples from B and C gives a sample v in S*T, so the 677 /// returned sample T*v is a sample in S. 678 Optional<SmallVector<int64_t, 8>> IntegerRelation::findIntegerSample() const { 679 // First, try the GCD test heuristic. 680 if (isEmptyByGCDTest()) 681 return {}; 682 683 Simplex simplex(*this); 684 if (simplex.isEmpty()) 685 return {}; 686 687 // For a bounded set, we directly call into the GBR sampling algorithm. 688 if (!simplex.isUnbounded()) 689 return simplex.findIntegerSample(); 690 691 // The set is unbounded. We cannot directly use the GBR algorithm. 692 // 693 // m is a matrix containing, in each row, a vector in which S is 694 // bounded, such that the linear span of all these dimensions contains all 695 // bounded dimensions in S. 696 Matrix m = getBoundedDirections(); 697 // In column echelon form, each row of m occupies only the first rank(m) 698 // columns and has zeros on the other columns. The transform T that brings S 699 // to column echelon form is unimodular as well, so this is a suitable 700 // transform to use in step 1 of the algorithm. 701 std::pair<unsigned, LinearTransform> result = 702 LinearTransform::makeTransformToColumnEchelon(std::move(m)); 703 const LinearTransform &transform = result.second; 704 // 1) Apply T to S to obtain S*T. 705 IntegerRelation transformedSet = transform.applyTo(*this); 706 707 // 2) Remove the unbounded dimensions and constraints involving them to 708 // obtain a bounded set. 709 IntegerRelation boundedSet(transformedSet); 710 unsigned numBoundedDims = result.first; 711 unsigned numUnboundedDims = getNumIds() - numBoundedDims; 712 removeConstraintsInvolvingIdRange(boundedSet, numBoundedDims, 713 numUnboundedDims); 714 boundedSet.removeIdRange(numBoundedDims, boundedSet.getNumIds()); 715 716 // 3) Try to obtain a sample from the bounded set. 717 Optional<SmallVector<int64_t, 8>> boundedSample = 718 Simplex(boundedSet).findIntegerSample(); 719 if (!boundedSample) 720 return {}; 721 assert(boundedSet.containsPoint(*boundedSample) && 722 "Simplex returned an invalid sample!"); 723 724 // 4) Substitute the values of the bounded dimensions into S*T to obtain a 725 // full-dimensional cone, which necessarily contains an integer sample. 726 transformedSet.setAndEliminate(0, *boundedSample); 727 IntegerRelation &cone = transformedSet; 728 729 // 5) Obtain an integer sample from the cone. 730 // 731 // We shrink the cone such that for any rational point in the shrunken cone, 732 // rounding up each of the point's coordinates produces a point that still 733 // lies in the original cone. 734 // 735 // Rounding up a point x adds a number e_i in [0, 1) to each coordinate x_i. 736 // For each inequality sum_i a_i x_i + c >= 0 in the original cone, the 737 // shrunken cone will have the inequality tightened by some amount s, such 738 // that if x satisfies the shrunken cone's tightened inequality, then x + e 739 // satisfies the original inequality, i.e., 740 // 741 // sum_i a_i x_i + c + s >= 0 implies sum_i a_i (x_i + e_i) + c >= 0 742 // 743 // for any e_i values in [0, 1). In fact, we will handle the slightly more 744 // general case where e_i can be in [0, 1]. For example, consider the 745 // inequality 2x_1 - 3x_2 - 7x_3 - 6 >= 0, and let x = (3, 0, 0). How low 746 // could the LHS go if we added a number in [0, 1] to each coordinate? The LHS 747 // is minimized when we add 1 to the x_i with negative coefficient a_i and 748 // keep the other x_i the same. In the example, we would get x = (3, 1, 1), 749 // changing the value of the LHS by -3 + -7 = -10. 750 // 751 // In general, the value of the LHS can change by at most the sum of the 752 // negative a_i, so we accomodate this by shifting the inequality by this 753 // amount for the shrunken cone. 754 for (unsigned i = 0, e = cone.getNumInequalities(); i < e; ++i) { 755 for (unsigned j = 0; j < cone.getNumIds(); ++j) { 756 int64_t coeff = cone.atIneq(i, j); 757 if (coeff < 0) 758 cone.atIneq(i, cone.getNumIds()) += coeff; 759 } 760 } 761 762 // Obtain an integer sample in the cone by rounding up a rational point from 763 // the shrunken cone. Shrinking the cone amounts to shifting its apex 764 // "inwards" without changing its "shape"; the shrunken cone is still a 765 // full-dimensional cone and is hence non-empty. 766 Simplex shrunkenConeSimplex(cone); 767 assert(!shrunkenConeSimplex.isEmpty() && "Shrunken cone cannot be empty!"); 768 769 // The sample will always exist since the shrunken cone is non-empty. 770 SmallVector<Fraction, 8> shrunkenConeSample = 771 *shrunkenConeSimplex.getRationalSample(); 772 773 SmallVector<int64_t, 8> coneSample(llvm::map_range(shrunkenConeSample, ceil)); 774 775 // 6) Return transform * concat(boundedSample, coneSample). 776 SmallVector<int64_t, 8> &sample = boundedSample.getValue(); 777 sample.append(coneSample.begin(), coneSample.end()); 778 return transform.postMultiplyWithColumn(sample); 779 } 780 781 /// Helper to evaluate an affine expression at a point. 782 /// The expression is a list of coefficients for the dimensions followed by the 783 /// constant term. 784 static int64_t valueAt(ArrayRef<int64_t> expr, ArrayRef<int64_t> point) { 785 assert(expr.size() == 1 + point.size() && 786 "Dimensionalities of point and expression don't match!"); 787 int64_t value = expr.back(); 788 for (unsigned i = 0; i < point.size(); ++i) 789 value += expr[i] * point[i]; 790 return value; 791 } 792 793 /// A point satisfies an equality iff the value of the equality at the 794 /// expression is zero, and it satisfies an inequality iff the value of the 795 /// inequality at that point is non-negative. 796 bool IntegerRelation::containsPoint(ArrayRef<int64_t> point) const { 797 for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 798 if (valueAt(getEquality(i), point) != 0) 799 return false; 800 } 801 for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 802 if (valueAt(getInequality(i), point) < 0) 803 return false; 804 } 805 return true; 806 } 807 808 /// Just substitute the values given and check if an integer sample exists for 809 /// the local ids. 810 /// 811 /// TODO: this could be made more efficient by handling divisions separately. 812 /// Instead of finding an integer sample over all the locals, we can first 813 /// compute the values of the locals that have division representations and 814 /// only use the integer emptiness check for the locals that don't have this. 815 /// Handling this correctly requires ordering the divs, though. 816 Optional<SmallVector<int64_t, 8>> 817 IntegerRelation::containsPointNoLocal(ArrayRef<int64_t> point) const { 818 assert(point.size() == getNumIds() - getNumLocalIds() && 819 "Point should contain all ids except locals!"); 820 assert(getIdKindOffset(IdKind::Local) == getNumIds() - getNumLocalIds() && 821 "This function depends on locals being stored last!"); 822 IntegerRelation copy = *this; 823 copy.setAndEliminate(0, point); 824 return copy.findIntegerSample(); 825 } 826 827 void IntegerRelation::getLocalReprs(std::vector<MaybeLocalRepr> &repr) const { 828 std::vector<SmallVector<int64_t, 8>> dividends(getNumLocalIds()); 829 SmallVector<unsigned, 4> denominators(getNumLocalIds()); 830 getLocalReprs(dividends, denominators, repr); 831 } 832 833 void IntegerRelation::getLocalReprs( 834 std::vector<SmallVector<int64_t, 8>> ÷nds, 835 SmallVector<unsigned, 4> &denominators) const { 836 std::vector<MaybeLocalRepr> repr(getNumLocalIds()); 837 getLocalReprs(dividends, denominators, repr); 838 } 839 840 void IntegerRelation::getLocalReprs( 841 std::vector<SmallVector<int64_t, 8>> ÷nds, 842 SmallVector<unsigned, 4> &denominators, 843 std::vector<MaybeLocalRepr> &repr) const { 844 845 repr.resize(getNumLocalIds()); 846 dividends.resize(getNumLocalIds()); 847 denominators.resize(getNumLocalIds()); 848 849 SmallVector<bool, 8> foundRepr(getNumIds(), false); 850 for (unsigned i = 0, e = getNumDimAndSymbolIds(); i < e; ++i) 851 foundRepr[i] = true; 852 853 unsigned divOffset = getNumDimAndSymbolIds(); 854 bool changed; 855 do { 856 // Each time changed is true, at end of this iteration, one or more local 857 // vars have been detected as floor divs. 858 changed = false; 859 for (unsigned i = 0, e = getNumLocalIds(); i < e; ++i) { 860 if (!foundRepr[i + divOffset]) { 861 MaybeLocalRepr res = computeSingleVarRepr( 862 *this, foundRepr, divOffset + i, dividends[i], denominators[i]); 863 if (!res) 864 continue; 865 foundRepr[i + divOffset] = true; 866 repr[i] = res; 867 changed = true; 868 } 869 } 870 } while (changed); 871 872 // Set 0 denominator for identifiers for which no division representation 873 // could be found. 874 for (unsigned i = 0, e = repr.size(); i < e; ++i) 875 if (!repr[i]) 876 denominators[i] = 0; 877 } 878 879 /// Tightens inequalities given that we are dealing with integer spaces. This is 880 /// analogous to the GCD test but applied to inequalities. The constant term can 881 /// be reduced to the preceding multiple of the GCD of the coefficients, i.e., 882 /// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a 883 /// fast method - linear in the number of coefficients. 884 // Example on how this affects practical cases: consider the scenario: 885 // 64*i >= 100, j = 64*i; without a tightening, elimination of i would yield 886 // j >= 100 instead of the tighter (exact) j >= 128. 887 void IntegerRelation::gcdTightenInequalities() { 888 unsigned numCols = getNumCols(); 889 for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 890 // Normalize the constraint and tighten the constant term by the GCD. 891 int64_t gcd = inequalities.normalizeRow(i, getNumCols() - 1); 892 if (gcd > 1) 893 atIneq(i, numCols - 1) = mlir::floorDiv(atIneq(i, numCols - 1), gcd); 894 } 895 } 896 897 // Eliminates all identifier variables in column range [posStart, posLimit). 898 // Returns the number of variables eliminated. 899 unsigned IntegerRelation::gaussianEliminateIds(unsigned posStart, 900 unsigned posLimit) { 901 // Return if identifier positions to eliminate are out of range. 902 assert(posLimit <= getNumIds()); 903 assert(hasConsistentState()); 904 905 if (posStart >= posLimit) 906 return 0; 907 908 gcdTightenInequalities(); 909 910 unsigned pivotCol = 0; 911 for (pivotCol = posStart; pivotCol < posLimit; ++pivotCol) { 912 // Find a row which has a non-zero coefficient in column 'j'. 913 unsigned pivotRow; 914 if (!findConstraintWithNonZeroAt(pivotCol, /*isEq=*/true, &pivotRow)) { 915 // No pivot row in equalities with non-zero at 'pivotCol'. 916 if (!findConstraintWithNonZeroAt(pivotCol, /*isEq=*/false, &pivotRow)) { 917 // If inequalities are also non-zero in 'pivotCol', it can be 918 // eliminated. 919 continue; 920 } 921 break; 922 } 923 924 // Eliminate identifier at 'pivotCol' from each equality row. 925 for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 926 eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart, 927 /*isEq=*/true); 928 equalities.normalizeRow(i); 929 } 930 931 // Eliminate identifier at 'pivotCol' from each inequality row. 932 for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 933 eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart, 934 /*isEq=*/false); 935 inequalities.normalizeRow(i); 936 } 937 removeEquality(pivotRow); 938 gcdTightenInequalities(); 939 } 940 // Update position limit based on number eliminated. 941 posLimit = pivotCol; 942 // Remove eliminated columns from all constraints. 943 removeIdRange(posStart, posLimit); 944 return posLimit - posStart; 945 } 946 947 // A more complex check to eliminate redundant inequalities. Uses FourierMotzkin 948 // to check if a constraint is redundant. 949 void IntegerRelation::removeRedundantInequalities() { 950 SmallVector<bool, 32> redun(getNumInequalities(), false); 951 // To check if an inequality is redundant, we replace the inequality by its 952 // complement (for eg., i - 1 >= 0 by i <= 0), and check if the resulting 953 // system is empty. If it is, the inequality is redundant. 954 IntegerRelation tmpCst(*this); 955 for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 956 // Change the inequality to its complement. 957 tmpCst.inequalities.negateRow(r); 958 --tmpCst.atIneq(r, tmpCst.getNumCols() - 1); 959 if (tmpCst.isEmpty()) { 960 redun[r] = true; 961 // Zero fill the redundant inequality. 962 inequalities.fillRow(r, /*value=*/0); 963 tmpCst.inequalities.fillRow(r, /*value=*/0); 964 } else { 965 // Reverse the change (to avoid recreating tmpCst each time). 966 ++tmpCst.atIneq(r, tmpCst.getNumCols() - 1); 967 tmpCst.inequalities.negateRow(r); 968 } 969 } 970 971 unsigned pos = 0; 972 for (unsigned r = 0, e = getNumInequalities(); r < e; ++r) { 973 if (!redun[r]) 974 inequalities.copyRow(r, pos++); 975 } 976 inequalities.resizeVertically(pos); 977 } 978 979 // A more complex check to eliminate redundant inequalities and equalities. Uses 980 // Simplex to check if a constraint is redundant. 981 void IntegerRelation::removeRedundantConstraints() { 982 // First, we run gcdTightenInequalities. This allows us to catch some 983 // constraints which are not redundant when considering rational solutions 984 // but are redundant in terms of integer solutions. 985 gcdTightenInequalities(); 986 Simplex simplex(*this); 987 simplex.detectRedundant(); 988 989 unsigned pos = 0; 990 unsigned numIneqs = getNumInequalities(); 991 // Scan to get rid of all inequalities marked redundant, in-place. In Simplex, 992 // the first constraints added are the inequalities. 993 for (unsigned r = 0; r < numIneqs; r++) { 994 if (!simplex.isMarkedRedundant(r)) 995 inequalities.copyRow(r, pos++); 996 } 997 inequalities.resizeVertically(pos); 998 999 // Scan to get rid of all equalities marked redundant, in-place. In Simplex, 1000 // after the inequalities, a pair of constraints for each equality is added. 1001 // An equality is redundant if both the inequalities in its pair are 1002 // redundant. 1003 pos = 0; 1004 for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 1005 if (!(simplex.isMarkedRedundant(numIneqs + 2 * r) && 1006 simplex.isMarkedRedundant(numIneqs + 2 * r + 1))) 1007 equalities.copyRow(r, pos++); 1008 } 1009 equalities.resizeVertically(pos); 1010 } 1011 1012 Optional<uint64_t> IntegerRelation::computeVolume() const { 1013 assert(getNumSymbolIds() == 0 && "Symbols are not yet supported!"); 1014 1015 Simplex simplex(*this); 1016 // If the polytope is rationally empty, there are certainly no integer 1017 // points. 1018 if (simplex.isEmpty()) 1019 return 0; 1020 1021 // Just find the maximum and minimum integer value of each non-local id 1022 // separately, thus finding the number of integer values each such id can 1023 // take. Multiplying these together gives a valid overapproximation of the 1024 // number of integer points in the relation. The result this gives is 1025 // equivalent to projecting (rationally) the relation onto its non-local ids 1026 // and returning the number of integer points in a minimal axis-parallel 1027 // hyperrectangular overapproximation of that. 1028 // 1029 // We also handle the special case where one dimension is unbounded and 1030 // another dimension can take no integer values. In this case, the volume is 1031 // zero. 1032 // 1033 // If there is no such empty dimension, if any dimension is unbounded we 1034 // just return the result as unbounded. 1035 uint64_t count = 1; 1036 SmallVector<int64_t, 8> dim(getNumIds() + 1); 1037 bool hasUnboundedId = false; 1038 for (unsigned i = 0, e = getNumDimAndSymbolIds(); i < e; ++i) { 1039 dim[i] = 1; 1040 MaybeOptimum<int64_t> min, max; 1041 std::tie(min, max) = simplex.computeIntegerBounds(dim); 1042 dim[i] = 0; 1043 1044 assert((!min.isEmpty() && !max.isEmpty()) && 1045 "Polytope should be rationally non-empty!"); 1046 1047 // One of the dimensions is unbounded. Note this fact. We will return 1048 // unbounded if none of the other dimensions makes the volume zero. 1049 if (min.isUnbounded() || max.isUnbounded()) { 1050 hasUnboundedId = true; 1051 continue; 1052 } 1053 1054 // In this case there are no valid integer points and the volume is 1055 // definitely zero. 1056 if (min.getBoundedOptimum() > max.getBoundedOptimum()) 1057 return 0; 1058 1059 count *= (*max - *min + 1); 1060 } 1061 1062 if (count == 0) 1063 return 0; 1064 if (hasUnboundedId) 1065 return {}; 1066 return count; 1067 } 1068 1069 void IntegerRelation::eliminateRedundantLocalId(unsigned posA, unsigned posB) { 1070 assert(posA < getNumLocalIds() && "Invalid local id position"); 1071 assert(posB < getNumLocalIds() && "Invalid local id position"); 1072 1073 unsigned localOffset = getIdKindOffset(IdKind::Local); 1074 posA += localOffset; 1075 posB += localOffset; 1076 inequalities.addToColumn(posB, posA, 1); 1077 equalities.addToColumn(posB, posA, 1); 1078 removeId(posB); 1079 } 1080 1081 /// Adds additional local ids to the sets such that they both have the union 1082 /// of the local ids in each set, without changing the set of points that 1083 /// lie in `this` and `other`. 1084 /// 1085 /// To detect local ids that always take the same value, each local id is 1086 /// represented as a floordiv with constant denominator in terms of other ids. 1087 /// After extracting these divisions, local ids in `other` with the same 1088 /// division representation as some other local id in any set are considered 1089 /// duplicate and are merged. 1090 /// 1091 /// It is possible that division representation for some local id cannot be 1092 /// obtained, and thus these local ids are not considered for detecting 1093 /// duplicates. 1094 unsigned IntegerRelation::mergeLocalIds(IntegerRelation &other) { 1095 IntegerRelation &relA = *this; 1096 IntegerRelation &relB = other; 1097 1098 unsigned oldALocals = relA.getNumLocalIds(); 1099 1100 // Merge function that merges the local variables in both sets by treating 1101 // them as the same identifier. 1102 auto merge = [&relA, &relB, oldALocals](unsigned i, unsigned j) -> bool { 1103 // We only merge from local at pos j to local at pos i, where j > i. 1104 if (i >= j) 1105 return false; 1106 1107 // If i < oldALocals, we are trying to merge duplicate divs. Since we do not 1108 // want to merge duplicates in A, we ignore this call. 1109 if (j < oldALocals) 1110 return false; 1111 1112 // Merge local at pos j into local at position i. 1113 relA.eliminateRedundantLocalId(i, j); 1114 relB.eliminateRedundantLocalId(i, j); 1115 return true; 1116 }; 1117 1118 presburger::mergeLocalIds(*this, other, merge); 1119 1120 // Since we do not remove duplicate divisions in relA, this is guranteed to be 1121 // non-negative. 1122 return relA.getNumLocalIds() - oldALocals; 1123 } 1124 1125 void IntegerRelation::removeDuplicateDivs() { 1126 std::vector<SmallVector<int64_t, 8>> divs; 1127 SmallVector<unsigned, 4> denoms; 1128 1129 getLocalReprs(divs, denoms); 1130 auto merge = [this](unsigned i, unsigned j) -> bool { 1131 eliminateRedundantLocalId(i, j); 1132 return true; 1133 }; 1134 presburger::removeDuplicateDivs(divs, denoms, getIdKindOffset(IdKind::Local), 1135 merge); 1136 } 1137 1138 /// Removes local variables using equalities. Each equality is checked if it 1139 /// can be reduced to the form: `e = affine-expr`, where `e` is a local 1140 /// variable and `affine-expr` is an affine expression not containing `e`. 1141 /// If an equality satisfies this form, the local variable is replaced in 1142 /// each constraint and then removed. The equality used to replace this local 1143 /// variable is also removed. 1144 void IntegerRelation::removeRedundantLocalVars() { 1145 // Normalize the equality constraints to reduce coefficients of local 1146 // variables to 1 wherever possible. 1147 for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) 1148 equalities.normalizeRow(i); 1149 1150 while (true) { 1151 unsigned i, e, j, f; 1152 for (i = 0, e = getNumEqualities(); i < e; ++i) { 1153 // Find a local variable to eliminate using ith equality. 1154 for (j = getNumDimAndSymbolIds(), f = getNumIds(); j < f; ++j) 1155 if (std::abs(atEq(i, j)) == 1) 1156 break; 1157 1158 // Local variable can be eliminated using ith equality. 1159 if (j < f) 1160 break; 1161 } 1162 1163 // No equality can be used to eliminate a local variable. 1164 if (i == e) 1165 break; 1166 1167 // Use the ith equality to simplify other equalities. If any changes 1168 // are made to an equality constraint, it is normalized by GCD. 1169 for (unsigned k = 0, t = getNumEqualities(); k < t; ++k) { 1170 if (atEq(k, j) != 0) { 1171 eliminateFromConstraint(this, k, i, j, j, /*isEq=*/true); 1172 equalities.normalizeRow(k); 1173 } 1174 } 1175 1176 // Use the ith equality to simplify inequalities. 1177 for (unsigned k = 0, t = getNumInequalities(); k < t; ++k) 1178 eliminateFromConstraint(this, k, i, j, j, /*isEq=*/false); 1179 1180 // Remove the ith equality and the found local variable. 1181 removeId(j); 1182 removeEquality(i); 1183 } 1184 } 1185 1186 void IntegerRelation::convertIdKind(IdKind srcKind, unsigned idStart, 1187 unsigned idLimit, IdKind dstKind, 1188 unsigned pos) { 1189 assert(idLimit <= getNumIdKind(srcKind) && "Invalid id range"); 1190 1191 if (idStart >= idLimit) 1192 return; 1193 1194 // Append new local variables corresponding to the dimensions to be converted. 1195 unsigned convertCount = idLimit - idStart; 1196 unsigned newIdsBegin = insertId(dstKind, pos, convertCount); 1197 1198 // Swap the new local variables with dimensions. 1199 // 1200 // Essentially, this moves the information corresponding to the specified ids 1201 // of kind `srcKind` to the `convertCount` newly created ids of kind 1202 // `dstKind`. In particular, this moves the columns in the constraint 1203 // matrices, and zeros out the initially occupied columns (because the newly 1204 // created ids we're swapping with were zero-initialized). 1205 unsigned offset = getIdKindOffset(srcKind); 1206 for (unsigned i = 0; i < convertCount; ++i) 1207 swapId(offset + idStart + i, newIdsBegin + i); 1208 1209 // Complete the move by deleting the initially occupied columns. 1210 removeIdRange(srcKind, idStart, idLimit); 1211 } 1212 1213 void IntegerRelation::addBound(BoundType type, unsigned pos, int64_t value) { 1214 assert(pos < getNumCols()); 1215 if (type == BoundType::EQ) { 1216 unsigned row = equalities.appendExtraRow(); 1217 equalities(row, pos) = 1; 1218 equalities(row, getNumCols() - 1) = -value; 1219 } else { 1220 unsigned row = inequalities.appendExtraRow(); 1221 inequalities(row, pos) = type == BoundType::LB ? 1 : -1; 1222 inequalities(row, getNumCols() - 1) = 1223 type == BoundType::LB ? -value : value; 1224 } 1225 } 1226 1227 void IntegerRelation::addBound(BoundType type, ArrayRef<int64_t> expr, 1228 int64_t value) { 1229 assert(type != BoundType::EQ && "EQ not implemented"); 1230 assert(expr.size() == getNumCols()); 1231 unsigned row = inequalities.appendExtraRow(); 1232 for (unsigned i = 0, e = expr.size(); i < e; ++i) 1233 inequalities(row, i) = type == BoundType::LB ? expr[i] : -expr[i]; 1234 inequalities(inequalities.getNumRows() - 1, getNumCols() - 1) += 1235 type == BoundType::LB ? -value : value; 1236 } 1237 1238 /// Adds a new local identifier as the floordiv of an affine function of other 1239 /// identifiers, the coefficients of which are provided in 'dividend' and with 1240 /// respect to a positive constant 'divisor'. Two constraints are added to the 1241 /// system to capture equivalence with the floordiv. 1242 /// q = expr floordiv c <=> c*q <= expr <= c*q + c - 1. 1243 void IntegerRelation::addLocalFloorDiv(ArrayRef<int64_t> dividend, 1244 int64_t divisor) { 1245 assert(dividend.size() == getNumCols() && "incorrect dividend size"); 1246 assert(divisor > 0 && "positive divisor expected"); 1247 1248 appendId(IdKind::Local); 1249 1250 // Add two constraints for this new identifier 'q'. 1251 SmallVector<int64_t, 8> bound(dividend.size() + 1); 1252 1253 // dividend - q * divisor >= 0 1254 std::copy(dividend.begin(), dividend.begin() + dividend.size() - 1, 1255 bound.begin()); 1256 bound.back() = dividend.back(); 1257 bound[getNumIds() - 1] = -divisor; 1258 addInequality(bound); 1259 1260 // -dividend +qdivisor * q + divisor - 1 >= 0 1261 std::transform(bound.begin(), bound.end(), bound.begin(), 1262 std::negate<int64_t>()); 1263 bound[bound.size() - 1] += divisor - 1; 1264 addInequality(bound); 1265 } 1266 1267 /// Finds an equality that equates the specified identifier to a constant. 1268 /// Returns the position of the equality row. If 'symbolic' is set to true, 1269 /// symbols are also treated like a constant, i.e., an affine function of the 1270 /// symbols is also treated like a constant. Returns -1 if such an equality 1271 /// could not be found. 1272 static int findEqualityToConstant(const IntegerRelation &cst, unsigned pos, 1273 bool symbolic = false) { 1274 assert(pos < cst.getNumIds() && "invalid position"); 1275 for (unsigned r = 0, e = cst.getNumEqualities(); r < e; r++) { 1276 int64_t v = cst.atEq(r, pos); 1277 if (v * v != 1) 1278 continue; 1279 unsigned c; 1280 unsigned f = symbolic ? cst.getNumDimIds() : cst.getNumIds(); 1281 // This checks for zeros in all positions other than 'pos' in [0, f) 1282 for (c = 0; c < f; c++) { 1283 if (c == pos) 1284 continue; 1285 if (cst.atEq(r, c) != 0) { 1286 // Dependent on another identifier. 1287 break; 1288 } 1289 } 1290 if (c == f) 1291 // Equality is free of other identifiers. 1292 return r; 1293 } 1294 return -1; 1295 } 1296 1297 LogicalResult IntegerRelation::constantFoldId(unsigned pos) { 1298 assert(pos < getNumIds() && "invalid position"); 1299 int rowIdx; 1300 if ((rowIdx = findEqualityToConstant(*this, pos)) == -1) 1301 return failure(); 1302 1303 // atEq(rowIdx, pos) is either -1 or 1. 1304 assert(atEq(rowIdx, pos) * atEq(rowIdx, pos) == 1); 1305 int64_t constVal = -atEq(rowIdx, getNumCols() - 1) / atEq(rowIdx, pos); 1306 setAndEliminate(pos, constVal); 1307 return success(); 1308 } 1309 1310 void IntegerRelation::constantFoldIdRange(unsigned pos, unsigned num) { 1311 for (unsigned s = pos, t = pos, e = pos + num; s < e; s++) { 1312 if (failed(constantFoldId(t))) 1313 t++; 1314 } 1315 } 1316 1317 /// Returns a non-negative constant bound on the extent (upper bound - lower 1318 /// bound) of the specified identifier if it is found to be a constant; returns 1319 /// None if it's not a constant. This methods treats symbolic identifiers 1320 /// specially, i.e., it looks for constant differences between affine 1321 /// expressions involving only the symbolic identifiers. See comments at 1322 /// function definition for example. 'lb', if provided, is set to the lower 1323 /// bound associated with the constant difference. Note that 'lb' is purely 1324 /// symbolic and thus will contain the coefficients of the symbolic identifiers 1325 /// and the constant coefficient. 1326 // Egs: 0 <= i <= 15, return 16. 1327 // s0 + 2 <= i <= s0 + 17, returns 16. (s0 has to be a symbol) 1328 // s0 + s1 + 16 <= d0 <= s0 + s1 + 31, returns 16. 1329 // s0 - 7 <= 8*j <= s0 returns 1 with lb = s0, lbDivisor = 8 (since lb = 1330 // ceil(s0 - 7 / 8) = floor(s0 / 8)). 1331 Optional<int64_t> IntegerRelation::getConstantBoundOnDimSize( 1332 unsigned pos, SmallVectorImpl<int64_t> *lb, int64_t *boundFloorDivisor, 1333 SmallVectorImpl<int64_t> *ub, unsigned *minLbPos, 1334 unsigned *minUbPos) const { 1335 assert(pos < getNumDimIds() && "Invalid identifier position"); 1336 1337 // Find an equality for 'pos'^th identifier that equates it to some function 1338 // of the symbolic identifiers (+ constant). 1339 int eqPos = findEqualityToConstant(*this, pos, /*symbolic=*/true); 1340 if (eqPos != -1) { 1341 auto eq = getEquality(eqPos); 1342 // If the equality involves a local var, punt for now. 1343 // TODO: this can be handled in the future by using the explicit 1344 // representation of the local vars. 1345 if (!std::all_of(eq.begin() + getNumDimAndSymbolIds(), eq.end() - 1, 1346 [](int64_t coeff) { return coeff == 0; })) 1347 return None; 1348 1349 // This identifier can only take a single value. 1350 if (lb) { 1351 // Set lb to that symbolic value. 1352 lb->resize(getNumSymbolIds() + 1); 1353 if (ub) 1354 ub->resize(getNumSymbolIds() + 1); 1355 for (unsigned c = 0, f = getNumSymbolIds() + 1; c < f; c++) { 1356 int64_t v = atEq(eqPos, pos); 1357 // atEq(eqRow, pos) is either -1 or 1. 1358 assert(v * v == 1); 1359 (*lb)[c] = v < 0 ? atEq(eqPos, getNumDimIds() + c) / -v 1360 : -atEq(eqPos, getNumDimIds() + c) / v; 1361 // Since this is an equality, ub = lb. 1362 if (ub) 1363 (*ub)[c] = (*lb)[c]; 1364 } 1365 assert(boundFloorDivisor && 1366 "both lb and divisor or none should be provided"); 1367 *boundFloorDivisor = 1; 1368 } 1369 if (minLbPos) 1370 *minLbPos = eqPos; 1371 if (minUbPos) 1372 *minUbPos = eqPos; 1373 return 1; 1374 } 1375 1376 // Check if the identifier appears at all in any of the inequalities. 1377 unsigned r, e; 1378 for (r = 0, e = getNumInequalities(); r < e; r++) { 1379 if (atIneq(r, pos) != 0) 1380 break; 1381 } 1382 if (r == e) 1383 // If it doesn't, there isn't a bound on it. 1384 return None; 1385 1386 // Positions of constraints that are lower/upper bounds on the variable. 1387 SmallVector<unsigned, 4> lbIndices, ubIndices; 1388 1389 // Gather all symbolic lower bounds and upper bounds of the variable, i.e., 1390 // the bounds can only involve symbolic (and local) identifiers. Since the 1391 // canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower 1392 // bound for x_i if c_i >= 1, and an upper bound if c_i <= -1. 1393 getLowerAndUpperBoundIndices(pos, &lbIndices, &ubIndices, 1394 /*eqIndices=*/nullptr, /*offset=*/0, 1395 /*num=*/getNumDimIds()); 1396 1397 Optional<int64_t> minDiff = None; 1398 unsigned minLbPosition = 0, minUbPosition = 0; 1399 for (auto ubPos : ubIndices) { 1400 for (auto lbPos : lbIndices) { 1401 // Look for a lower bound and an upper bound that only differ by a 1402 // constant, i.e., pairs of the form 0 <= c_pos - f(c_i's) <= diffConst. 1403 // For example, if ii is the pos^th variable, we are looking for 1404 // constraints like ii >= i, ii <= ii + 50, 50 being the difference. The 1405 // minimum among all such constant differences is kept since that's the 1406 // constant bounding the extent of the pos^th variable. 1407 unsigned j, e; 1408 for (j = 0, e = getNumCols() - 1; j < e; j++) 1409 if (atIneq(ubPos, j) != -atIneq(lbPos, j)) { 1410 break; 1411 } 1412 if (j < getNumCols() - 1) 1413 continue; 1414 int64_t diff = ceilDiv(atIneq(ubPos, getNumCols() - 1) + 1415 atIneq(lbPos, getNumCols() - 1) + 1, 1416 atIneq(lbPos, pos)); 1417 // This bound is non-negative by definition. 1418 diff = std::max<int64_t>(diff, 0); 1419 if (minDiff == None || diff < minDiff) { 1420 minDiff = diff; 1421 minLbPosition = lbPos; 1422 minUbPosition = ubPos; 1423 } 1424 } 1425 } 1426 if (lb && minDiff.hasValue()) { 1427 // Set lb to the symbolic lower bound. 1428 lb->resize(getNumSymbolIds() + 1); 1429 if (ub) 1430 ub->resize(getNumSymbolIds() + 1); 1431 // The lower bound is the ceildiv of the lb constraint over the coefficient 1432 // of the variable at 'pos'. We express the ceildiv equivalently as a floor 1433 // for uniformity. For eg., if the lower bound constraint was: 32*d0 - N + 1434 // 31 >= 0, the lower bound for d0 is ceil(N - 31, 32), i.e., floor(N, 32). 1435 *boundFloorDivisor = atIneq(minLbPosition, pos); 1436 assert(*boundFloorDivisor == -atIneq(minUbPosition, pos)); 1437 for (unsigned c = 0, e = getNumSymbolIds() + 1; c < e; c++) { 1438 (*lb)[c] = -atIneq(minLbPosition, getNumDimIds() + c); 1439 } 1440 if (ub) { 1441 for (unsigned c = 0, e = getNumSymbolIds() + 1; c < e; c++) 1442 (*ub)[c] = atIneq(minUbPosition, getNumDimIds() + c); 1443 } 1444 // The lower bound leads to a ceildiv while the upper bound is a floordiv 1445 // whenever the coefficient at pos != 1. ceildiv (val / d) = floordiv (val + 1446 // d - 1 / d); hence, the addition of 'atIneq(minLbPosition, pos) - 1' to 1447 // the constant term for the lower bound. 1448 (*lb)[getNumSymbolIds()] += atIneq(minLbPosition, pos) - 1; 1449 } 1450 if (minLbPos) 1451 *minLbPos = minLbPosition; 1452 if (minUbPos) 1453 *minUbPos = minUbPosition; 1454 return minDiff; 1455 } 1456 1457 template <bool isLower> 1458 Optional<int64_t> 1459 IntegerRelation::computeConstantLowerOrUpperBound(unsigned pos) { 1460 assert(pos < getNumIds() && "invalid position"); 1461 // Project to 'pos'. 1462 projectOut(0, pos); 1463 projectOut(1, getNumIds() - 1); 1464 // Check if there's an equality equating the '0'^th identifier to a constant. 1465 int eqRowIdx = findEqualityToConstant(*this, 0, /*symbolic=*/false); 1466 if (eqRowIdx != -1) 1467 // atEq(rowIdx, 0) is either -1 or 1. 1468 return -atEq(eqRowIdx, getNumCols() - 1) / atEq(eqRowIdx, 0); 1469 1470 // Check if the identifier appears at all in any of the inequalities. 1471 unsigned r, e; 1472 for (r = 0, e = getNumInequalities(); r < e; r++) { 1473 if (atIneq(r, 0) != 0) 1474 break; 1475 } 1476 if (r == e) 1477 // If it doesn't, there isn't a bound on it. 1478 return None; 1479 1480 Optional<int64_t> minOrMaxConst = None; 1481 1482 // Take the max across all const lower bounds (or min across all constant 1483 // upper bounds). 1484 for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1485 if (isLower) { 1486 if (atIneq(r, 0) <= 0) 1487 // Not a lower bound. 1488 continue; 1489 } else if (atIneq(r, 0) >= 0) { 1490 // Not an upper bound. 1491 continue; 1492 } 1493 unsigned c, f; 1494 for (c = 0, f = getNumCols() - 1; c < f; c++) 1495 if (c != 0 && atIneq(r, c) != 0) 1496 break; 1497 if (c < getNumCols() - 1) 1498 // Not a constant bound. 1499 continue; 1500 1501 int64_t boundConst = 1502 isLower ? mlir::ceilDiv(-atIneq(r, getNumCols() - 1), atIneq(r, 0)) 1503 : mlir::floorDiv(atIneq(r, getNumCols() - 1), -atIneq(r, 0)); 1504 if (isLower) { 1505 if (minOrMaxConst == None || boundConst > minOrMaxConst) 1506 minOrMaxConst = boundConst; 1507 } else { 1508 if (minOrMaxConst == None || boundConst < minOrMaxConst) 1509 minOrMaxConst = boundConst; 1510 } 1511 } 1512 return minOrMaxConst; 1513 } 1514 1515 Optional<int64_t> IntegerRelation::getConstantBound(BoundType type, 1516 unsigned pos) const { 1517 if (type == BoundType::LB) 1518 return IntegerRelation(*this) 1519 .computeConstantLowerOrUpperBound</*isLower=*/true>(pos); 1520 if (type == BoundType::UB) 1521 return IntegerRelation(*this) 1522 .computeConstantLowerOrUpperBound</*isLower=*/false>(pos); 1523 1524 assert(type == BoundType::EQ && "expected EQ"); 1525 Optional<int64_t> lb = 1526 IntegerRelation(*this).computeConstantLowerOrUpperBound</*isLower=*/true>( 1527 pos); 1528 Optional<int64_t> ub = 1529 IntegerRelation(*this) 1530 .computeConstantLowerOrUpperBound</*isLower=*/false>(pos); 1531 return (lb && ub && *lb == *ub) ? Optional<int64_t>(*ub) : None; 1532 } 1533 1534 // A simple (naive and conservative) check for hyper-rectangularity. 1535 bool IntegerRelation::isHyperRectangular(unsigned pos, unsigned num) const { 1536 assert(pos < getNumCols() - 1); 1537 // Check for two non-zero coefficients in the range [pos, pos + sum). 1538 for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1539 unsigned sum = 0; 1540 for (unsigned c = pos; c < pos + num; c++) { 1541 if (atIneq(r, c) != 0) 1542 sum++; 1543 } 1544 if (sum > 1) 1545 return false; 1546 } 1547 for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 1548 unsigned sum = 0; 1549 for (unsigned c = pos; c < pos + num; c++) { 1550 if (atEq(r, c) != 0) 1551 sum++; 1552 } 1553 if (sum > 1) 1554 return false; 1555 } 1556 return true; 1557 } 1558 1559 /// Removes duplicate constraints, trivially true constraints, and constraints 1560 /// that can be detected as redundant as a result of differing only in their 1561 /// constant term part. A constraint of the form <non-negative constant> >= 0 is 1562 /// considered trivially true. 1563 // Uses a DenseSet to hash and detect duplicates followed by a linear scan to 1564 // remove duplicates in place. 1565 void IntegerRelation::removeTrivialRedundancy() { 1566 gcdTightenInequalities(); 1567 normalizeConstraintsByGCD(); 1568 1569 // A map used to detect redundancy stemming from constraints that only differ 1570 // in their constant term. The value stored is <row position, const term> 1571 // for a given row. 1572 SmallDenseMap<ArrayRef<int64_t>, std::pair<unsigned, int64_t>> 1573 rowsWithoutConstTerm; 1574 // To unique rows. 1575 SmallDenseSet<ArrayRef<int64_t>, 8> rowSet; 1576 1577 // Check if constraint is of the form <non-negative-constant> >= 0. 1578 auto isTriviallyValid = [&](unsigned r) -> bool { 1579 for (unsigned c = 0, e = getNumCols() - 1; c < e; c++) { 1580 if (atIneq(r, c) != 0) 1581 return false; 1582 } 1583 return atIneq(r, getNumCols() - 1) >= 0; 1584 }; 1585 1586 // Detect and mark redundant constraints. 1587 SmallVector<bool, 256> redunIneq(getNumInequalities(), false); 1588 for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1589 int64_t *rowStart = &inequalities(r, 0); 1590 auto row = ArrayRef<int64_t>(rowStart, getNumCols()); 1591 if (isTriviallyValid(r) || !rowSet.insert(row).second) { 1592 redunIneq[r] = true; 1593 continue; 1594 } 1595 1596 // Among constraints that only differ in the constant term part, mark 1597 // everything other than the one with the smallest constant term redundant. 1598 // (eg: among i - 16j - 5 >= 0, i - 16j - 1 >=0, i - 16j - 7 >= 0, the 1599 // former two are redundant). 1600 int64_t constTerm = atIneq(r, getNumCols() - 1); 1601 auto rowWithoutConstTerm = ArrayRef<int64_t>(rowStart, getNumCols() - 1); 1602 const auto &ret = 1603 rowsWithoutConstTerm.insert({rowWithoutConstTerm, {r, constTerm}}); 1604 if (!ret.second) { 1605 // Check if the other constraint has a higher constant term. 1606 auto &val = ret.first->second; 1607 if (val.second > constTerm) { 1608 // The stored row is redundant. Mark it so, and update with this one. 1609 redunIneq[val.first] = true; 1610 val = {r, constTerm}; 1611 } else { 1612 // The one stored makes this one redundant. 1613 redunIneq[r] = true; 1614 } 1615 } 1616 } 1617 1618 // Scan to get rid of all rows marked redundant, in-place. 1619 unsigned pos = 0; 1620 for (unsigned r = 0, e = getNumInequalities(); r < e; r++) 1621 if (!redunIneq[r]) 1622 inequalities.copyRow(r, pos++); 1623 1624 inequalities.resizeVertically(pos); 1625 1626 // TODO: consider doing this for equalities as well, but probably not worth 1627 // the savings. 1628 } 1629 1630 #undef DEBUG_TYPE 1631 #define DEBUG_TYPE "fm" 1632 1633 /// Eliminates identifier at the specified position using Fourier-Motzkin 1634 /// variable elimination. This technique is exact for rational spaces but 1635 /// conservative (in "rare" cases) for integer spaces. The operation corresponds 1636 /// to a projection operation yielding the (convex) set of integer points 1637 /// contained in the rational shadow of the set. An emptiness test that relies 1638 /// on this method will guarantee emptiness, i.e., it disproves the existence of 1639 /// a solution if it says it's empty. 1640 /// If a non-null isResultIntegerExact is passed, it is set to true if the 1641 /// result is also integer exact. If it's set to false, the obtained solution 1642 /// *may* not be exact, i.e., it may contain integer points that do not have an 1643 /// integer pre-image in the original set. 1644 /// 1645 /// Eg: 1646 /// j >= 0, j <= i + 1 1647 /// i >= 0, i <= N + 1 1648 /// Eliminating i yields, 1649 /// j >= 0, 0 <= N + 1, j - 1 <= N + 1 1650 /// 1651 /// If darkShadow = true, this method computes the dark shadow on elimination; 1652 /// the dark shadow is a convex integer subset of the exact integer shadow. A 1653 /// non-empty dark shadow proves the existence of an integer solution. The 1654 /// elimination in such a case could however be an under-approximation, and thus 1655 /// should not be used for scanning sets or used by itself for dependence 1656 /// checking. 1657 /// 1658 /// Eg: 2-d set, * represents grid points, 'o' represents a point in the set. 1659 /// ^ 1660 /// | 1661 /// | * * * * o o 1662 /// i | * * o o o o 1663 /// | o * * * * * 1664 /// ---------------> 1665 /// j -> 1666 /// 1667 /// Eliminating i from this system (projecting on the j dimension): 1668 /// rational shadow / integer light shadow: 1 <= j <= 6 1669 /// dark shadow: 3 <= j <= 6 1670 /// exact integer shadow: j = 1 \union 3 <= j <= 6 1671 /// holes/splinters: j = 2 1672 /// 1673 /// darkShadow = false, isResultIntegerExact = nullptr are default values. 1674 // TODO: a slight modification to yield dark shadow version of FM (tightened), 1675 // which can prove the existence of a solution if there is one. 1676 void IntegerRelation::fourierMotzkinEliminate(unsigned pos, bool darkShadow, 1677 bool *isResultIntegerExact) { 1678 LLVM_DEBUG(llvm::dbgs() << "FM input (eliminate pos " << pos << "):\n"); 1679 LLVM_DEBUG(dump()); 1680 assert(pos < getNumIds() && "invalid position"); 1681 assert(hasConsistentState()); 1682 1683 // Check if this identifier can be eliminated through a substitution. 1684 for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 1685 if (atEq(r, pos) != 0) { 1686 // Use Gaussian elimination here (since we have an equality). 1687 LogicalResult ret = gaussianEliminateId(pos); 1688 (void)ret; 1689 assert(succeeded(ret) && "Gaussian elimination guaranteed to succeed"); 1690 LLVM_DEBUG(llvm::dbgs() << "FM output (through Gaussian elimination):\n"); 1691 LLVM_DEBUG(dump()); 1692 return; 1693 } 1694 } 1695 1696 // A fast linear time tightening. 1697 gcdTightenInequalities(); 1698 1699 // Check if the identifier appears at all in any of the inequalities. 1700 if (isColZero(pos)) { 1701 // If it doesn't appear, just remove the column and return. 1702 // TODO: refactor removeColumns to use it from here. 1703 removeId(pos); 1704 LLVM_DEBUG(llvm::dbgs() << "FM output:\n"); 1705 LLVM_DEBUG(dump()); 1706 return; 1707 } 1708 1709 // Positions of constraints that are lower bounds on the variable. 1710 SmallVector<unsigned, 4> lbIndices; 1711 // Positions of constraints that are lower bounds on the variable. 1712 SmallVector<unsigned, 4> ubIndices; 1713 // Positions of constraints that do not involve the variable. 1714 std::vector<unsigned> nbIndices; 1715 nbIndices.reserve(getNumInequalities()); 1716 1717 // Gather all lower bounds and upper bounds of the variable. Since the 1718 // canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower 1719 // bound for x_i if c_i >= 1, and an upper bound if c_i <= -1. 1720 for (unsigned r = 0, e = getNumInequalities(); r < e; r++) { 1721 if (atIneq(r, pos) == 0) { 1722 // Id does not appear in bound. 1723 nbIndices.push_back(r); 1724 } else if (atIneq(r, pos) >= 1) { 1725 // Lower bound. 1726 lbIndices.push_back(r); 1727 } else { 1728 // Upper bound. 1729 ubIndices.push_back(r); 1730 } 1731 } 1732 1733 PresburgerSpace newSpace = getSpace(); 1734 IdKind idKindRemove = newSpace.getIdKindAt(pos); 1735 unsigned relativePos = pos - newSpace.getIdKindOffset(idKindRemove); 1736 newSpace.removeIdRange(idKindRemove, relativePos, relativePos + 1); 1737 1738 /// Create the new system which has one identifier less. 1739 IntegerRelation newRel(lbIndices.size() * ubIndices.size() + nbIndices.size(), 1740 getNumEqualities(), getNumCols() - 1, newSpace); 1741 1742 // This will be used to check if the elimination was integer exact. 1743 unsigned lcmProducts = 1; 1744 1745 // Let x be the variable we are eliminating. 1746 // For each lower bound, lb <= c_l*x, and each upper bound c_u*x <= ub, (note 1747 // that c_l, c_u >= 1) we have: 1748 // lb*lcm(c_l, c_u)/c_l <= lcm(c_l, c_u)*x <= ub*lcm(c_l, c_u)/c_u 1749 // We thus generate a constraint: 1750 // lcm(c_l, c_u)/c_l*lb <= lcm(c_l, c_u)/c_u*ub. 1751 // Note if c_l = c_u = 1, all integer points captured by the resulting 1752 // constraint correspond to integer points in the original system (i.e., they 1753 // have integer pre-images). Hence, if the lcm's are all 1, the elimination is 1754 // integer exact. 1755 for (auto ubPos : ubIndices) { 1756 for (auto lbPos : lbIndices) { 1757 SmallVector<int64_t, 4> ineq; 1758 ineq.reserve(newRel.getNumCols()); 1759 int64_t lbCoeff = atIneq(lbPos, pos); 1760 // Note that in the comments above, ubCoeff is the negation of the 1761 // coefficient in the canonical form as the view taken here is that of the 1762 // term being moved to the other size of '>='. 1763 int64_t ubCoeff = -atIneq(ubPos, pos); 1764 // TODO: refactor this loop to avoid all branches inside. 1765 for (unsigned l = 0, e = getNumCols(); l < e; l++) { 1766 if (l == pos) 1767 continue; 1768 assert(lbCoeff >= 1 && ubCoeff >= 1 && "bounds wrongly identified"); 1769 int64_t lcm = mlir::lcm(lbCoeff, ubCoeff); 1770 ineq.push_back(atIneq(ubPos, l) * (lcm / ubCoeff) + 1771 atIneq(lbPos, l) * (lcm / lbCoeff)); 1772 lcmProducts *= lcm; 1773 } 1774 if (darkShadow) { 1775 // The dark shadow is a convex subset of the exact integer shadow. If 1776 // there is a point here, it proves the existence of a solution. 1777 ineq[ineq.size() - 1] += lbCoeff * ubCoeff - lbCoeff - ubCoeff + 1; 1778 } 1779 // TODO: we need to have a way to add inequalities in-place in 1780 // IntegerRelation instead of creating and copying over. 1781 newRel.addInequality(ineq); 1782 } 1783 } 1784 1785 LLVM_DEBUG(llvm::dbgs() << "FM isResultIntegerExact: " << (lcmProducts == 1) 1786 << "\n"); 1787 if (lcmProducts == 1 && isResultIntegerExact) 1788 *isResultIntegerExact = true; 1789 1790 // Copy over the constraints not involving this variable. 1791 for (auto nbPos : nbIndices) { 1792 SmallVector<int64_t, 4> ineq; 1793 ineq.reserve(getNumCols() - 1); 1794 for (unsigned l = 0, e = getNumCols(); l < e; l++) { 1795 if (l == pos) 1796 continue; 1797 ineq.push_back(atIneq(nbPos, l)); 1798 } 1799 newRel.addInequality(ineq); 1800 } 1801 1802 assert(newRel.getNumConstraints() == 1803 lbIndices.size() * ubIndices.size() + nbIndices.size()); 1804 1805 // Copy over the equalities. 1806 for (unsigned r = 0, e = getNumEqualities(); r < e; r++) { 1807 SmallVector<int64_t, 4> eq; 1808 eq.reserve(newRel.getNumCols()); 1809 for (unsigned l = 0, e = getNumCols(); l < e; l++) { 1810 if (l == pos) 1811 continue; 1812 eq.push_back(atEq(r, l)); 1813 } 1814 newRel.addEquality(eq); 1815 } 1816 1817 // GCD tightening and normalization allows detection of more trivially 1818 // redundant constraints. 1819 newRel.gcdTightenInequalities(); 1820 newRel.normalizeConstraintsByGCD(); 1821 newRel.removeTrivialRedundancy(); 1822 clearAndCopyFrom(newRel); 1823 LLVM_DEBUG(llvm::dbgs() << "FM output:\n"); 1824 LLVM_DEBUG(dump()); 1825 } 1826 1827 #undef DEBUG_TYPE 1828 #define DEBUG_TYPE "presburger" 1829 1830 void IntegerRelation::projectOut(unsigned pos, unsigned num) { 1831 if (num == 0) 1832 return; 1833 1834 // 'pos' can be at most getNumCols() - 2 if num > 0. 1835 assert((getNumCols() < 2 || pos <= getNumCols() - 2) && "invalid position"); 1836 assert(pos + num < getNumCols() && "invalid range"); 1837 1838 // Eliminate as many identifiers as possible using Gaussian elimination. 1839 unsigned currentPos = pos; 1840 unsigned numToEliminate = num; 1841 unsigned numGaussianEliminated = 0; 1842 1843 while (currentPos < getNumIds()) { 1844 unsigned curNumEliminated = 1845 gaussianEliminateIds(currentPos, currentPos + numToEliminate); 1846 ++currentPos; 1847 numToEliminate -= curNumEliminated + 1; 1848 numGaussianEliminated += curNumEliminated; 1849 } 1850 1851 // Eliminate the remaining using Fourier-Motzkin. 1852 for (unsigned i = 0; i < num - numGaussianEliminated; i++) { 1853 unsigned numToEliminate = num - numGaussianEliminated - i; 1854 fourierMotzkinEliminate( 1855 getBestIdToEliminate(*this, pos, pos + numToEliminate)); 1856 } 1857 1858 // Fast/trivial simplifications. 1859 gcdTightenInequalities(); 1860 // Normalize constraints after tightening since the latter impacts this, but 1861 // not the other way round. 1862 normalizeConstraintsByGCD(); 1863 } 1864 1865 namespace { 1866 1867 enum BoundCmpResult { Greater, Less, Equal, Unknown }; 1868 1869 /// Compares two affine bounds whose coefficients are provided in 'first' and 1870 /// 'second'. The last coefficient is the constant term. 1871 static BoundCmpResult compareBounds(ArrayRef<int64_t> a, ArrayRef<int64_t> b) { 1872 assert(a.size() == b.size()); 1873 1874 // For the bounds to be comparable, their corresponding identifier 1875 // coefficients should be equal; the constant terms are then compared to 1876 // determine less/greater/equal. 1877 1878 if (!std::equal(a.begin(), a.end() - 1, b.begin())) 1879 return Unknown; 1880 1881 if (a.back() == b.back()) 1882 return Equal; 1883 1884 return a.back() < b.back() ? Less : Greater; 1885 } 1886 } // namespace 1887 1888 // Returns constraints that are common to both A & B. 1889 static void getCommonConstraints(const IntegerRelation &a, 1890 const IntegerRelation &b, IntegerRelation &c) { 1891 c = IntegerRelation(a.getSpace()); 1892 // a naive O(n^2) check should be enough here given the input sizes. 1893 for (unsigned r = 0, e = a.getNumInequalities(); r < e; ++r) { 1894 for (unsigned s = 0, f = b.getNumInequalities(); s < f; ++s) { 1895 if (a.getInequality(r) == b.getInequality(s)) { 1896 c.addInequality(a.getInequality(r)); 1897 break; 1898 } 1899 } 1900 } 1901 for (unsigned r = 0, e = a.getNumEqualities(); r < e; ++r) { 1902 for (unsigned s = 0, f = b.getNumEqualities(); s < f; ++s) { 1903 if (a.getEquality(r) == b.getEquality(s)) { 1904 c.addEquality(a.getEquality(r)); 1905 break; 1906 } 1907 } 1908 } 1909 } 1910 1911 // Computes the bounding box with respect to 'other' by finding the min of the 1912 // lower bounds and the max of the upper bounds along each of the dimensions. 1913 LogicalResult 1914 IntegerRelation::unionBoundingBox(const IntegerRelation &otherCst) { 1915 assert(space.isEqual(otherCst.getSpace()) && "Spaces should match."); 1916 assert(getNumLocalIds() == 0 && "local ids not supported yet here"); 1917 1918 // Get the constraints common to both systems; these will be added as is to 1919 // the union. 1920 IntegerRelation commonCst(PresburgerSpace::getRelationSpace()); 1921 getCommonConstraints(*this, otherCst, commonCst); 1922 1923 std::vector<SmallVector<int64_t, 8>> boundingLbs; 1924 std::vector<SmallVector<int64_t, 8>> boundingUbs; 1925 boundingLbs.reserve(2 * getNumDimIds()); 1926 boundingUbs.reserve(2 * getNumDimIds()); 1927 1928 // To hold lower and upper bounds for each dimension. 1929 SmallVector<int64_t, 4> lb, otherLb, ub, otherUb; 1930 // To compute min of lower bounds and max of upper bounds for each dimension. 1931 SmallVector<int64_t, 4> minLb(getNumSymbolIds() + 1); 1932 SmallVector<int64_t, 4> maxUb(getNumSymbolIds() + 1); 1933 // To compute final new lower and upper bounds for the union. 1934 SmallVector<int64_t, 8> newLb(getNumCols()), newUb(getNumCols()); 1935 1936 int64_t lbFloorDivisor, otherLbFloorDivisor; 1937 for (unsigned d = 0, e = getNumDimIds(); d < e; ++d) { 1938 auto extent = getConstantBoundOnDimSize(d, &lb, &lbFloorDivisor, &ub); 1939 if (!extent.hasValue()) 1940 // TODO: symbolic extents when necessary. 1941 // TODO: handle union if a dimension is unbounded. 1942 return failure(); 1943 1944 auto otherExtent = otherCst.getConstantBoundOnDimSize( 1945 d, &otherLb, &otherLbFloorDivisor, &otherUb); 1946 if (!otherExtent.hasValue() || lbFloorDivisor != otherLbFloorDivisor) 1947 // TODO: symbolic extents when necessary. 1948 return failure(); 1949 1950 assert(lbFloorDivisor > 0 && "divisor always expected to be positive"); 1951 1952 auto res = compareBounds(lb, otherLb); 1953 // Identify min. 1954 if (res == BoundCmpResult::Less || res == BoundCmpResult::Equal) { 1955 minLb = lb; 1956 // Since the divisor is for a floordiv, we need to convert to ceildiv, 1957 // i.e., i >= expr floordiv div <=> i >= (expr - div + 1) ceildiv div <=> 1958 // div * i >= expr - div + 1. 1959 minLb.back() -= lbFloorDivisor - 1; 1960 } else if (res == BoundCmpResult::Greater) { 1961 minLb = otherLb; 1962 minLb.back() -= otherLbFloorDivisor - 1; 1963 } else { 1964 // Uncomparable - check for constant lower/upper bounds. 1965 auto constLb = getConstantBound(BoundType::LB, d); 1966 auto constOtherLb = otherCst.getConstantBound(BoundType::LB, d); 1967 if (!constLb.hasValue() || !constOtherLb.hasValue()) 1968 return failure(); 1969 std::fill(minLb.begin(), minLb.end(), 0); 1970 minLb.back() = std::min(constLb.getValue(), constOtherLb.getValue()); 1971 } 1972 1973 // Do the same for ub's but max of upper bounds. Identify max. 1974 auto uRes = compareBounds(ub, otherUb); 1975 if (uRes == BoundCmpResult::Greater || uRes == BoundCmpResult::Equal) { 1976 maxUb = ub; 1977 } else if (uRes == BoundCmpResult::Less) { 1978 maxUb = otherUb; 1979 } else { 1980 // Uncomparable - check for constant lower/upper bounds. 1981 auto constUb = getConstantBound(BoundType::UB, d); 1982 auto constOtherUb = otherCst.getConstantBound(BoundType::UB, d); 1983 if (!constUb.hasValue() || !constOtherUb.hasValue()) 1984 return failure(); 1985 std::fill(maxUb.begin(), maxUb.end(), 0); 1986 maxUb.back() = std::max(constUb.getValue(), constOtherUb.getValue()); 1987 } 1988 1989 std::fill(newLb.begin(), newLb.end(), 0); 1990 std::fill(newUb.begin(), newUb.end(), 0); 1991 1992 // The divisor for lb, ub, otherLb, otherUb at this point is lbDivisor, 1993 // and so it's the divisor for newLb and newUb as well. 1994 newLb[d] = lbFloorDivisor; 1995 newUb[d] = -lbFloorDivisor; 1996 // Copy over the symbolic part + constant term. 1997 std::copy(minLb.begin(), minLb.end(), newLb.begin() + getNumDimIds()); 1998 std::transform(newLb.begin() + getNumDimIds(), newLb.end(), 1999 newLb.begin() + getNumDimIds(), std::negate<int64_t>()); 2000 std::copy(maxUb.begin(), maxUb.end(), newUb.begin() + getNumDimIds()); 2001 2002 boundingLbs.push_back(newLb); 2003 boundingUbs.push_back(newUb); 2004 } 2005 2006 // Clear all constraints and add the lower/upper bounds for the bounding box. 2007 clearConstraints(); 2008 for (unsigned d = 0, e = getNumDimIds(); d < e; ++d) { 2009 addInequality(boundingLbs[d]); 2010 addInequality(boundingUbs[d]); 2011 } 2012 2013 // Add the constraints that were common to both systems. 2014 append(commonCst); 2015 removeTrivialRedundancy(); 2016 2017 // TODO: copy over pure symbolic constraints from this and 'other' over to the 2018 // union (since the above are just the union along dimensions); we shouldn't 2019 // be discarding any other constraints on the symbols. 2020 2021 return success(); 2022 } 2023 2024 bool IntegerRelation::isColZero(unsigned pos) const { 2025 unsigned rowPos; 2026 return !findConstraintWithNonZeroAt(pos, /*isEq=*/false, &rowPos) && 2027 !findConstraintWithNonZeroAt(pos, /*isEq=*/true, &rowPos); 2028 } 2029 2030 /// Find positions of inequalities and equalities that do not have a coefficient 2031 /// for [pos, pos + num) identifiers. 2032 static void getIndependentConstraints(const IntegerRelation &cst, unsigned pos, 2033 unsigned num, 2034 SmallVectorImpl<unsigned> &nbIneqIndices, 2035 SmallVectorImpl<unsigned> &nbEqIndices) { 2036 assert(pos < cst.getNumIds() && "invalid start position"); 2037 assert(pos + num <= cst.getNumIds() && "invalid limit"); 2038 2039 for (unsigned r = 0, e = cst.getNumInequalities(); r < e; r++) { 2040 // The bounds are to be independent of [offset, offset + num) columns. 2041 unsigned c; 2042 for (c = pos; c < pos + num; ++c) { 2043 if (cst.atIneq(r, c) != 0) 2044 break; 2045 } 2046 if (c == pos + num) 2047 nbIneqIndices.push_back(r); 2048 } 2049 2050 for (unsigned r = 0, e = cst.getNumEqualities(); r < e; r++) { 2051 // The bounds are to be independent of [offset, offset + num) columns. 2052 unsigned c; 2053 for (c = pos; c < pos + num; ++c) { 2054 if (cst.atEq(r, c) != 0) 2055 break; 2056 } 2057 if (c == pos + num) 2058 nbEqIndices.push_back(r); 2059 } 2060 } 2061 2062 void IntegerRelation::removeIndependentConstraints(unsigned pos, unsigned num) { 2063 assert(pos + num <= getNumIds() && "invalid range"); 2064 2065 // Remove constraints that are independent of these identifiers. 2066 SmallVector<unsigned, 4> nbIneqIndices, nbEqIndices; 2067 getIndependentConstraints(*this, /*pos=*/0, num, nbIneqIndices, nbEqIndices); 2068 2069 // Iterate in reverse so that indices don't have to be updated. 2070 // TODO: This method can be made more efficient (because removal of each 2071 // inequality leads to much shifting/copying in the underlying buffer). 2072 for (auto nbIndex : llvm::reverse(nbIneqIndices)) 2073 removeInequality(nbIndex); 2074 for (auto nbIndex : llvm::reverse(nbEqIndices)) 2075 removeEquality(nbIndex); 2076 } 2077 2078 IntegerPolyhedron IntegerRelation::getDomainSet() const { 2079 IntegerRelation copyRel = *this; 2080 2081 // Convert Range variables to Local variables. 2082 copyRel.convertIdKind(IdKind::Range, 0, getNumIdKind(IdKind::Range), 2083 IdKind::Local); 2084 2085 // Convert Domain variables to SetDim(Range) variables. 2086 copyRel.convertIdKind(IdKind::Domain, 0, getNumIdKind(IdKind::Domain), 2087 IdKind::SetDim); 2088 2089 return IntegerPolyhedron(std::move(copyRel)); 2090 } 2091 2092 IntegerPolyhedron IntegerRelation::getRangeSet() const { 2093 IntegerRelation copyRel = *this; 2094 2095 // Convert Domain variables to Local variables. 2096 copyRel.convertIdKind(IdKind::Domain, 0, getNumIdKind(IdKind::Domain), 2097 IdKind::Local); 2098 2099 // We do not need to do anything to Range variables since they are already in 2100 // SetDim position. 2101 2102 return IntegerPolyhedron(std::move(copyRel)); 2103 } 2104 2105 void IntegerRelation::intersectDomain(const IntegerPolyhedron &poly) { 2106 assert(getDomainSet().getSpace().isCompatible(poly.getSpace()) && 2107 "Domain set is not compatible with poly"); 2108 2109 // Treating the poly as a relation, convert it from `0 -> R` to `R -> 0`. 2110 IntegerRelation rel = poly; 2111 rel.inverse(); 2112 2113 // Append dummy range variables to make the spaces compatible. 2114 rel.appendId(IdKind::Range, getNumRangeIds()); 2115 2116 // Intersect in place. 2117 mergeLocalIds(rel); 2118 append(rel); 2119 } 2120 2121 void IntegerRelation::intersectRange(const IntegerPolyhedron &poly) { 2122 assert(getRangeSet().getSpace().isCompatible(poly.getSpace()) && 2123 "Range set is not compatible with poly"); 2124 2125 IntegerRelation rel = poly; 2126 2127 // Append dummy domain variables to make the spaces compatible. 2128 rel.appendId(IdKind::Domain, getNumDomainIds()); 2129 2130 mergeLocalIds(rel); 2131 append(rel); 2132 } 2133 2134 void IntegerRelation::inverse() { 2135 unsigned numRangeIds = getNumIdKind(IdKind::Range); 2136 convertIdKind(IdKind::Domain, 0, getIdKindEnd(IdKind::Domain), IdKind::Range); 2137 convertIdKind(IdKind::Range, 0, numRangeIds, IdKind::Domain); 2138 } 2139 2140 void IntegerRelation::compose(const IntegerRelation &rel) { 2141 assert(getRangeSet().getSpace().isCompatible(rel.getDomainSet().getSpace()) && 2142 "Range of `this` should be compatible with Domain of `rel`"); 2143 2144 IntegerRelation copyRel = rel; 2145 2146 // Let relation `this` be R1: A -> B, and `rel` be R2: B -> C. 2147 // We convert R1 to A -> (B X C), and R2 to B X C then intersect the range of 2148 // R1 with R2. After this, we get R1: A -> C, by projecting out B. 2149 // TODO: Using nested spaces here would help, since we could directly 2150 // intersect the range with another relation. 2151 unsigned numBIds = getNumRangeIds(); 2152 2153 // Convert R1 from A -> B to A -> (B X C). 2154 appendId(IdKind::Range, copyRel.getNumRangeIds()); 2155 2156 // Convert R2 to B X C. 2157 copyRel.convertIdKind(IdKind::Domain, 0, numBIds, IdKind::Range, 0); 2158 2159 // Intersect R2 to range of R1. 2160 intersectRange(IntegerPolyhedron(copyRel)); 2161 2162 // Project out B in R1. 2163 convertIdKind(IdKind::Range, 0, numBIds, IdKind::Local); 2164 } 2165 2166 void IntegerRelation::applyDomain(const IntegerRelation &rel) { 2167 inverse(); 2168 compose(rel); 2169 inverse(); 2170 } 2171 2172 void IntegerRelation::applyRange(const IntegerRelation &rel) { compose(rel); } 2173 2174 void IntegerRelation::printSpace(raw_ostream &os) const { 2175 space.print(os); 2176 os << getNumConstraints() << " constraints\n"; 2177 } 2178 2179 void IntegerRelation::print(raw_ostream &os) const { 2180 assert(hasConsistentState()); 2181 printSpace(os); 2182 for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) { 2183 for (unsigned j = 0, f = getNumCols(); j < f; ++j) { 2184 os << atEq(i, j) << " "; 2185 } 2186 os << "= 0\n"; 2187 } 2188 for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) { 2189 for (unsigned j = 0, f = getNumCols(); j < f; ++j) { 2190 os << atIneq(i, j) << " "; 2191 } 2192 os << ">= 0\n"; 2193 } 2194 os << '\n'; 2195 } 2196 2197 void IntegerRelation::dump() const { print(llvm::errs()); } 2198 2199 unsigned IntegerPolyhedron::insertId(IdKind kind, unsigned pos, unsigned num) { 2200 assert((kind != IdKind::Domain || num == 0) && 2201 "Domain has to be zero in a set"); 2202 return IntegerRelation::insertId(kind, pos, num); 2203 } 2204