Coverage Report

Created: 2020-06-26 05:44

/home/arjun/llvm-project/mlir/lib/Analysis/Presburger/Simplex.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- Simplex.cpp - MLIR Simplex 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
#include "mlir/Analysis/Presburger/Simplex.h"
10
#include "mlir/Analysis/Presburger/Matrix.h"
11
#include "mlir/Support/MathExtras.h"
12
13
namespace mlir {
14
using Direction = Simplex::Direction;
15
16
const int nullIndex = std::numeric_limits<int>::max();
17
18
/// Construct a Simplex object with `nVar` variables.
19
Simplex::Simplex(unsigned nVar)
20
60
    : nRow(0), nCol(2), tableau(0, 2 + nVar), empty(false) {
21
60
  colUnknown.push_back(nullIndex);
22
60
  colUnknown.push_back(nullIndex);
23
245
  for (unsigned i = 0; i < nVar; ++i) {
24
185
    var.emplace_back(Orientation::Column, /*restricted=*/false, /*pos=*/nCol);
25
185
    colUnknown.push_back(i);
26
185
    nCol++;
27
185
  }
28
60
}
29
30
Simplex::Simplex(const FlatAffineConstraints &constraints)
31
31
    : Simplex(constraints.getNumIds()) {
32
31
  for (unsigned i = 0, numIneqs = constraints.getNumInequalities();
33
133
       i < numIneqs; ++i)
34
102
    addInequality(constraints.getInequality(i));
35
47
  for (unsigned i = 0, numEqs = constraints.getNumEqualities(); i < numEqs; ++i)
36
16
    addEquality(constraints.getEquality(i));
37
31
}
38
39
5.87k
const Simplex::Unknown &Simplex::unknownFromIndex(int index) const {
40
5.87k
  assert(index != nullIndex && "nullIndex passed to unknownFromIndex");
41
5.87k
  return index >= 0 ? var[index] : con[~index];
42
5.87k
}
43
44
2.64k
const Simplex::Unknown &Simplex::unknownFromColumn(unsigned col) const {
45
2.64k
  assert(col < nCol && "Invalid column");
46
2.64k
  return unknownFromIndex(colUnknown[col]);
47
2.64k
}
48
49
3.23k
const Simplex::Unknown &Simplex::unknownFromRow(unsigned row) const {
50
3.23k
  assert(row < nRow && "Invalid row");
51
3.23k
  return unknownFromIndex(rowUnknown[row]);
52
3.23k
}
53
54
1.74k
Simplex::Unknown &Simplex::unknownFromIndex(int index) {
55
1.74k
  assert(index != nullIndex && "nullIndex passed to unknownFromIndex");
56
1.74k
  return index >= 0 ? var[index] : con[~index];
57
1.74k
}
58
59
746
Simplex::Unknown &Simplex::unknownFromColumn(unsigned col) {
60
746
  assert(col < nCol && "Invalid column");
61
746
  return unknownFromIndex(colUnknown[col]);
62
746
}
63
64
824
Simplex::Unknown &Simplex::unknownFromRow(unsigned row) {
65
824
  assert(row < nRow && "Invalid row");
66
824
  return unknownFromIndex(rowUnknown[row]);
67
824
}
68
69
/// Add a new row to the tableau corresponding to the given constant term and
70
/// list of coefficients. The coefficients are specified as a vector of
71
/// (variable index, coefficient) pairs.
72
673
unsigned Simplex::addRow(ArrayRef<int64_t> coeffs) {
73
673
  assert(coeffs.size() == 1 + var.size() &&
74
673
         "Incorrect number of coefficients!");
75
673
76
673
  ++nRow;
77
673
  // If the tableau is not big enough to accomodate the extra row, we extend it.
78
673
  if (nRow >= tableau.getNumRows())
79
673
    tableau.resizeVertically(nRow);
80
673
  rowUnknown.push_back(~con.size());
81
673
  con.emplace_back(Orientation::Row, false, nRow - 1);
82
673
83
673
  tableau(nRow - 1, 0) = 1;
84
673
  tableau(nRow - 1, 1) = coeffs.back();
85
3.37k
  for (unsigned col = 2; col < nCol; ++col)
86
2.70k
    tableau(nRow - 1, col) = 0;
87
673
88
673
  // Process each given variable coefficient.
89
3.37k
  for (unsigned i = 0; i < var.size(); ++i) {
90
2.70k
    unsigned pos = var[i].pos;
91
2.70k
    if (coeffs[i] == 0)
92
1.28k
      continue;
93
1.41k
94
1.41k
    if (var[i].orientation == Orientation::Column) {
95
343
      // If a variable is in column position at column col, then we just add the
96
343
      // coefficient for that variable (scaled by the common row denominator) to
97
343
      // the corresponding entry in the new row.
98
343
      tableau(nRow - 1, pos) += coeffs[i] * tableau(nRow - 1, 0);
99
343
      continue;
100
343
    }
101
1.07k
102
1.07k
    // If the variable is in row position, we need to add that row to the new
103
1.07k
    // row, scaled by the coefficient for the variable, accounting for the two
104
1.07k
    // rows potentially having different denominators. The new denominator is
105
1.07k
    // the lcm of the two.
106
1.07k
    int64_t lcm = mlir::lcm(tableau(nRow - 1, 0), tableau(pos, 0));
107
1.07k
    int64_t nRowCoeff = lcm / tableau(nRow - 1, 0);
108
1.07k
    int64_t idxRowCoeff = coeffs[i] * (lcm / tableau(pos, 0));
109
1.07k
    tableau(nRow - 1, 0) = lcm;
110
7.92k
    for (unsigned col = 1; col < nCol; ++col)
111
6.85k
      tableau(nRow - 1, col) =
112
6.85k
          nRowCoeff * tableau(nRow - 1, col) + idxRowCoeff * tableau(pos, col);
113
1.07k
  }
114
673
115
673
  normalizeRow(nRow - 1);
116
673
  // Push to undo log along with the index of the new constraint.
117
673
  undoLog.push_back(UndoLogEntry::RemoveLastConstraint);
118
673
  return con.size() - 1;
119
673
}
120
121
/// Normalize the row by removing factors that are common between the
122
/// denominator and all the numerator coefficients.
123
4.53k
void Simplex::normalizeRow(unsigned row) {
124
4.53k
  int64_t gcd = 0;
125
20.1k
  for (unsigned col = 0; col < nCol; ++col) {
126
19.1k
    if (gcd == 1)
127
3.55k
      break;
128
15.5k
    gcd = llvm::greatestCommonDivisor(gcd, std::abs(tableau(row, col)));
129
15.5k
  }
130
35.9k
  for (unsigned col = 0; col < nCol; ++col)
131
31.4k
    tableau(row, col) /= gcd;
132
4.53k
}
133
134
namespace {
135
4.32k
bool signMatchesDirection(int64_t elem, Direction direction) {
136
4.32k
  assert(elem != 0 && "elem should not be 0");
137
4.32k
  return direction == Direction::Up ? elem > 0 : elem < 0;
138
4.32k
}
139
140
102
Direction flippedDirection(Direction direction) {
141
102
  return direction == Direction::Up ? Direction::Down : Simplex::Direction::Up;
142
102
}
143
} // anonymous namespace
144
145
/// Find a pivot to change the sample value of the row in the specified
146
/// direction. The returned pivot row will involve `row` if and only if the
147
/// unknown is unbounded in the specified direction.
148
///
149
/// To increase (resp. decrease) the value of a row, we need to find a live
150
/// column with a non-zero coefficient. If the coefficient is positive, we need
151
/// to increase (decrease) the value of the column, and if the coefficient is
152
/// negative, we need to decrease (increase) the value of the column. Also,
153
/// we cannot decrease the sample value of restricted columns.
154
///
155
/// If multiple columns are valid, we break ties by considering a lexicographic
156
/// ordering where we prefer unknowns with lower index.
157
Optional<Simplex::Pivot> Simplex::findPivot(int row,
158
870
                                            Direction direction) const {
159
870
  Optional<unsigned> col;
160
4.54k
  for (unsigned j = 2; j < nCol; ++j) {
161
3.67k
    int64_t elem = tableau(row, j);
162
3.67k
    if (elem == 0)
163
1.03k
      continue;
164
2.64k
165
2.64k
    if (unknownFromColumn(j).restricted &&
166
2.64k
        !signMatchesDirection(elem, direction))
167
1.47k
      continue;
168
1.16k
    if (!col || colUnknown[j] < colUnknown[*col])
169
842
      col = j;
170
1.16k
  }
171
870
172
870
  if (!col)
173
264
    return {};
174
606
175
606
  Direction newDirection =
176
606
      tableau(row, *col) < 0 ? flippedDirection(direction) : direction;
177
606
  Optional<unsigned> maybePivotRow = findPivotRow(row, newDirection, *col);
178
606
  return Pivot{maybePivotRow.getValueOr(row), *col};
179
606
}
180
181
/// Swap the associated unknowns for the row and the column.
182
///
183
/// First we swap the index associated with the row and column. Then we update
184
/// the unknowns to reflect their new position and orientation.
185
746
void Simplex::swapRowWithCol(unsigned row, unsigned col) {
186
746
  std::swap(rowUnknown[row], colUnknown[col]);
187
746
  Unknown &uCol = unknownFromColumn(col);
188
746
  Unknown &uRow = unknownFromRow(row);
189
746
  uCol.orientation = Orientation::Column;
190
746
  uRow.orientation = Orientation::Row;
191
746
  uCol.pos = col;
192
746
  uRow.pos = row;
193
746
}
194
195
599
void Simplex::pivot(Pivot pair) { pivot(pair.row, pair.column); }
196
197
/// Pivot pivotRow and pivotCol.
198
///
199
/// Let R be the pivot row unknown and let C be the pivot col unknown.
200
/// Since initially R = a*C + sum b_i * X_i
201
/// (where the sum is over the other column's unknowns, x_i)
202
/// C = (R - (sum b_i * X_i))/a
203
///
204
/// Let u be some other row unknown.
205
/// u = c*C + sum d_i * X_i
206
/// So u = c*(R - sum b_i * X_i)/a + sum d_i * X_i
207
///
208
/// This results in the following transform:
209
///            pivot col    other col                   pivot col    other col
210
/// pivot row     a             b       ->   pivot row     1/a         -b/a
211
/// other row     c             d            other row     c/a        d - bc/a
212
///
213
/// Taking into account the common denominators p and q:
214
///
215
///            pivot col    other col                    pivot col   other col
216
/// pivot row     a/p          b/p     ->   pivot row      p/a         -b/a
217
/// other row     c/q          d/q          other row     cp/aq    (da - bc)/aq
218
///
219
/// The pivot row transform is accomplished be swapping a with the pivot row's
220
/// common denominator and negating the pivot row except for the pivot column
221
/// element.
222
746
void Simplex::pivot(unsigned pivotRow, unsigned pivotCol) {
223
746
  assert(pivotCol >= 2 && "Refusing to pivot invalid column");
224
746
225
746
  swapRowWithCol(pivotRow, pivotCol);
226
746
  std::swap(tableau(pivotRow, 0), tableau(pivotRow, pivotCol));
227
746
  // We need to negate the whole pivot row except for the pivot column.
228
746
  if (tableau(pivotRow, 0) < 0) {
229
639
    // If the denominator is negative, we negate the row by simply negating the
230
639
    // denominator.
231
639
    tableau(pivotRow, 0) = -tableau(pivotRow, 0);
232
639
    tableau(pivotRow, pivotCol) = -tableau(pivotRow, pivotCol);
233
639
  } else {
234
537
    for (unsigned col = 1; col < nCol; ++col) {
235
430
      if (col == pivotCol)
236
107
        continue;
237
323
      tableau(pivotRow, col) = -tableau(pivotRow, col);
238
323
    }
239
107
  }
240
746
  normalizeRow(pivotRow);
241
746
242
7.86k
  for (unsigned row = 0; row < nRow; ++row) {
243
7.11k
    if (row == pivotRow)
244
746
      continue;
245
6.37k
    if (tableau(row, pivotCol) == 0) // Nothing to do.
246
3.25k
      continue;
247
3.11k
    tableau(row, 0) *= tableau(pivotRow, 0);
248
22.5k
    for (unsigned j = 1; j < nCol; ++j) {
249
19.4k
      if (j == pivotCol)
250
3.11k
        continue;
251
16.2k
      // Add rather than subtract because the pivot row has been negated.
252
16.2k
      tableau(row, j) = tableau(row, j) * tableau(pivotRow, 0) +
253
16.2k
                        tableau(row, pivotCol) * tableau(pivotRow, j);
254
16.2k
    }
255
3.11k
    tableau(row, pivotCol) *= tableau(pivotRow, pivotCol);
256
3.11k
    normalizeRow(row);
257
3.11k
  }
258
746
}
259
260
/// Perform pivots until the unknown has a non-negative sample value or until
261
/// no more upward pivots can be performed. Return the sign of the final sample
262
/// value.
263
415
LogicalResult Simplex::restoreRow(Unknown &u) {
264
415
  assert(u.orientation == Orientation::Row &&
265
415
         "unknown should be in row position");
266
415
267
562
  while (tableau(u.pos, 1) < 0) {
268
219
    Optional<Pivot> maybePivot = findPivot(u.pos, Direction::Up);
269
219
    if (!maybePivot)
270
13
      break;
271
206
272
206
    pivot(*maybePivot);
273
206
    if (u.orientation == Orientation::Column)
274
59
      return LogicalResult::Success; // the unknown is unbounded above.
275
206
  }
276
415
  return success(tableau(u.pos, 1) >= 0);
277
415
}
278
279
/// Find a row that can be used to pivot the column in the specified direction.
280
/// This returns an empty optional if and only if the column is unbounded in the
281
/// specified direction (ignoring skipRow, if skipRow is set).
282
///
283
/// If skipRow is set, this row is not considered, and (if it is restricted) its
284
/// restriction may be violated by the returned pivot. Usually, skipRow is set
285
/// because we don't want to move it to column position unless it is unbounded,
286
/// and we are either trying to increase the value of skipRow or explicitly
287
/// trying to make skipRow negative, so we are not concerned about this.
288
///
289
/// If the direction is up (resp. down) and a restricted row has a negative
290
/// (positive) coefficient for the column, then this row imposes a bound on how
291
/// much the sample value of the column can change. Such a row with constant
292
/// term c and coefficient f for the column imposes a bound of c/|f| on the
293
/// change in sample value (in the specified direction). (note that c is
294
/// non-negative here since the row is restricted and the tableau is consistent)
295
///
296
/// We iterate through the rows and pick the row which imposes the most
297
/// stringent bound, since pivoting with a row changes the row's sample value to
298
/// 0 and hence saturates the bound it imposes. We break ties between rows that
299
/// impose the same bound by considering a lexicographic ordering where we
300
/// prefer unknowns with lower index value.
301
Optional<unsigned> Simplex::findPivotRow(Optional<unsigned> skipRow,
302
                                         Direction direction,
303
768
                                         unsigned col) const {
304
768
  Optional<unsigned> retRow;
305
768
  int64_t retElem, retConst;
306
7.77k
  for (unsigned row = 0; row < nRow; ++row) {
307
7.00k
    if (skipRow && row == *skipRow)
308
606
      continue;
309
6.40k
    int64_t elem = tableau(row, col);
310
6.40k
    if (elem == 0)
311
3.17k
      continue;
312
3.23k
    if (!unknownFromRow(row).restricted)
313
1.56k
      continue;
314
1.66k
    if (signMatchesDirection(elem, direction))
315
521
      continue;
316
1.14k
    int64_t constTerm = tableau(row, 1);
317
1.14k
318
1.14k
    if (!retRow) {
319
653
      retRow = row;
320
653
      retElem = elem;
321
653
      retConst = constTerm;
322
653
      continue;
323
653
    }
324
489
325
489
    int64_t diff = retConst * elem - constTerm * retElem;
326
489
    if ((diff == 0 && rowUnknown[row] < rowUnknown[*retRow]) ||
327
489
        (diff != 0 && !signMatchesDirection(diff, direction))) {
328
403
      retRow = row;
329
403
      retElem = elem;
330
403
      retConst = constTerm;
331
403
    }
332
489
  }
333
768
  return retRow;
334
768
}
335
336
53
bool Simplex::isEmpty() const { return empty; }
337
338
421
void Simplex::swapRows(unsigned i, unsigned j) {
339
421
  if (i == j)
340
382
    return;
341
39
  tableau.swapRows(i, j);
342
39
  std::swap(rowUnknown[i], rowUnknown[j]);
343
39
  unknownFromRow(i).pos = i;
344
39
  unknownFromRow(j).pos = j;
345
39
}
346
347
/// Mark this tableau empty and push an entry to the undo stack.
348
13
void Simplex::markEmpty() {
349
13
  undoLog.push_back(UndoLogEntry::UnmarkEmpty);
350
13
  empty = true;
351
13
}
352
353
/// Add an inequality to the tableau. If coeffs is c_0, c_1, ... c_n, where n
354
/// is the curent number of variables, then the corresponding inequality is
355
/// c_n + c_0*x_0 + c_1*x_1 + ... + c_{n-1}*x_{n-1} >= 0.
356
///
357
/// We add the inequality and mark it as restricted. We then try to make its
358
/// sample value non-negative. If this is not possible, the tableau has become
359
/// empty and we mark it as such.
360
415
void Simplex::addInequality(ArrayRef<int64_t> coeffs) {
361
415
  unsigned conIndex = addRow(coeffs);
362
415
  Unknown &u = con[conIndex];
363
415
  u.restricted = true;
364
415
  LogicalResult result = restoreRow(u);
365
415
  if (failed(result))
366
13
    markEmpty();
367
415
}
368
369
/// Add an equality to the tableau. If coeffs is c_0, c_1, ... c_n, where n
370
/// is the curent number of variables, then the corresponding equality is
371
/// c_n + c_0*x_0 + c_1*x_1 + ... + c_{n-1}*x_{n-1} == 0.
372
///
373
/// We simply add two opposing inequalities, which force the expression to
374
/// be zero.
375
111
void Simplex::addEquality(ArrayRef<int64_t> coeffs) {
376
111
  addInequality(coeffs);
377
111
  SmallVector<int64_t, 8> negatedCoeffs;
378
111
  for (int64_t coeff : coeffs)
379
679
    negatedCoeffs.emplace_back(-coeff);
380
111
  addInequality(negatedCoeffs);
381
111
}
382
383
233
unsigned Simplex::numVariables() const { return var.size(); }
384
94
unsigned Simplex::numConstraints() const { return con.size(); }
385
386
/// Return a snapshot of the curent state. This is just the current size of the
387
/// undo log.
388
392
unsigned Simplex::getSnapshot() const { return undoLog.size(); }
389
390
430
void Simplex::undo(UndoLogEntry entry) {
391
430
  if (entry == UndoLogEntry::RemoveLastConstraint) {
392
421
    Unknown &constraint = con.back();
393
421
    if (constraint.orientation == Orientation::Column) {
394
137
      unsigned column = constraint.pos;
395
137
      Optional<unsigned> row;
396
137
397
137
      // Try to find any pivot row for this column that preserves tableau
398
137
      // consistency (except possibly the column itself, which is going to be
399
137
      // deallocated anyway).
400
137
      //
401
137
      // If no pivot row is found in either direction, then the unknown is
402
137
      // unbounded in both directions and we are free to
403
137
      // perform any pivot at all. To do this, we just need to find any row with
404
137
      // a non-zero coefficient for the column.
405
137
      if (Optional<unsigned> maybeRow =
406
112
              findPivotRow({}, Direction::Up, column)) {
407
112
        row = *maybeRow;
408
112
      } else if (Optional<unsigned> maybeRow =
409
1
                     findPivotRow({}, Direction::Down, column)) {
410
1
        row = *maybeRow;
411
24
      } else {
412
24
        // The loop doesn't find a pivot row only if the column has zero
413
24
        // coefficients for every row. But the unknown is a constraint,
414
24
        // so it was added initially as a row. Such a row could never have been
415
24
        // pivoted to a column. So a pivot row will always be found.
416
27
        for (unsigned i = 0; i < nRow; ++i) {
417
27
          if (tableau(i, column) != 0) {
418
24
            row = i;
419
24
            break;
420
24
          }
421
27
        }
422
24
      }
423
137
      assert(row.hasValue() && "No pivot row found!");
424
137
      pivot(*row, column);
425
137
    }
426
421
427
421
    // Move this unknown to the last row and remove the last row from the
428
421
    // tableau.
429
421
    swapRows(constraint.pos, nRow - 1);
430
421
    // It is not strictly necessary to shrink the tableau, but for now we
431
421
    // maintain the invariant that the tableau has exactly nRow rows.
432
421
    tableau.resizeVertically(nRow - 1);
433
421
    nRow--;
434
421
    rowUnknown.pop_back();
435
421
    con.pop_back();
436
421
  } else if (entry == UndoLogEntry::UnmarkEmpty) {
437
9
    empty = false;
438
9
  }
439
430
}
440
441
/// Rollback to the specified snapshot.
442
///
443
/// We undo all the log entries until the log size when the snapshot was taken
444
/// is reached.
445
375
void Simplex::rollback(unsigned snapshot) {
446
805
  while (undoLog.size() > snapshot) {
447
430
    undo(undoLog.back());
448
430
    undoLog.pop_back();
449
430
  }
450
375
}
451
452
Optional<Fraction> Simplex::computeRowOptimum(Direction direction,
453
258
                                              unsigned row) {
454
258
  // Keep trying to find a pivot for the row in the specified direction.
455
651
  while (Optional<Pivot> maybePivot = findPivot(row, direction)) {
456
400
    // If findPivot returns a pivot involving the row itself, then the optimum
457
400
    // is unbounded, so we return None.
458
400
    if (maybePivot->row == row)
459
7
      return {};
460
393
    pivot(*maybePivot);
461
393
  }
462
258
463
258
  // The row has reached its optimal sample value, which we return.
464
258
  // The sample value is the entry in the constant column divided by the common
465
258
  // denominator for this row.
466
258
  return Fraction(tableau(row, 1), tableau(row, 0));
467
258
}
468
469
/// Compute the optimum of the specified expression in the specified direction,
470
/// or None if it is unbounded.
471
Optional<Fraction> Simplex::computeOptimum(Direction direction,
472
161
                                           ArrayRef<int64_t> coeffs) {
473
161
  assert(!empty && "Tableau should not be empty");
474
161
475
161
  unsigned snapshot = getSnapshot();
476
161
  unsigned conIndex = addRow(coeffs);
477
161
  unsigned row = con[conIndex].pos;
478
161
  Optional<Fraction> optimum = computeRowOptimum(direction, row);
479
161
  rollback(snapshot);
480
161
  return optimum;
481
161
}
482
483
16
bool Simplex::isUnbounded() {
484
16
  if (empty)
485
1
    return false;
486
15
487
15
  SmallVector<int64_t, 8> dir(var.size() + 1);
488
40
  for (unsigned i = 0; i < var.size(); ++i) {
489
32
    dir[i] = 1;
490
32
491
32
    Optional<Fraction> maybeMax = computeOptimum(Direction::Up, dir);
492
32
    if (!maybeMax)
493
5
      return true;
494
27
495
27
    Optional<Fraction> maybeMin = computeOptimum(Direction::Down, dir);
496
27
    if (!maybeMin)
497
2
      return true;
498
25
499
25
    dir[i] = 0;
500
25
  }
501
15
  return false;
502
15
}
503
504
/// Make a tableau to represent a pair of points in the original tableau.
505
///
506
/// The product constraints and variables are stored as: first A's, then B's.
507
///
508
/// The product tableau has row layout:
509
///   A's rows, B's rows.
510
///
511
/// It has column layout:
512
///   denominator, constant, A's columns, B's columns.
513
12
Simplex Simplex::makeProduct(const Simplex &a, const Simplex &b) {
514
12
  unsigned numVar = a.numVariables() + b.numVariables();
515
12
  unsigned numCon = a.numConstraints() + b.numConstraints();
516
12
  Simplex result(numVar);
517
12
518
12
  result.tableau.resizeVertically(numCon);
519
12
  result.empty = a.empty || b.empty;
520
12
521
24
  auto concat = [](ArrayRef<Unknown> v, ArrayRef<Unknown> w) {
522
24
    SmallVector<Unknown, 8> result;
523
24
    result.reserve(v.size() + w.size());
524
24
    result.insert(result.end(), v.begin(), v.end());
525
24
    result.insert(result.end(), w.begin(), w.end());
526
24
    return result;
527
24
  };
528
12
  result.con = concat(a.con, b.con);
529
12
  result.var = concat(a.var, b.var);
530
12
531
88
  auto indexFromBIndex = [&](int index) {
532
88
    return index >= 0 ? a.numVariables() + index
533
88
                      : ~(a.numConstraints() + ~index);
534
88
  };
535
12
536
12
  result.colUnknown.assign(2, nullIndex);
537
46
  for (unsigned i = 2; i < a.nCol; ++i) {
538
34
    result.colUnknown.push_back(a.colUnknown[i]);
539
34
    result.unknownFromIndex(result.colUnknown.back()).pos =
540
34
        result.colUnknown.size() - 1;
541
34
  }
542
46
  for (unsigned i = 2; i < b.nCol; ++i) {
543
34
    result.colUnknown.push_back(indexFromBIndex(b.colUnknown[i]));
544
34
    result.unknownFromIndex(result.colUnknown.back()).pos =
545
34
        result.colUnknown.size() - 1;
546
34
  }
547
12
548
54
  auto appendRowFromA = [&](unsigned row) {
549
322
    for (unsigned col = 0; col < a.nCol; ++col)
550
268
      result.tableau(result.nRow, col) = a.tableau(row, col);
551
54
    result.rowUnknown.push_back(a.rowUnknown[row]);
552
54
    result.unknownFromIndex(result.rowUnknown.back()).pos =
553
54
        result.rowUnknown.size() - 1;
554
54
    result.nRow++;
555
54
  };
556
12
557
12
  // Also fixes the corresponding entry in rowUnknown and var/con (as the case
558
12
  // may be).
559
54
  auto appendRowFromB = [&](unsigned row) {
560
54
    result.tableau(result.nRow, 0) = b.tableau(row, 0);
561
54
    result.tableau(result.nRow, 1) = b.tableau(row, 1);
562
54
563
54
    unsigned offset = a.nCol - 2;
564
214
    for (unsigned col = 2; col < b.nCol; ++col)
565
160
      result.tableau(result.nRow, offset + col) = b.tableau(row, col);
566
54
    result.rowUnknown.push_back(indexFromBIndex(b.rowUnknown[row]));
567
54
    result.unknownFromIndex(result.rowUnknown.back()).pos =
568
54
        result.rowUnknown.size() - 1;
569
54
    result.nRow++;
570
54
  };
571
12
572
66
  for (unsigned row = 0; row < a.nRow; ++row)
573
54
    appendRowFromA(row);
574
66
  for (unsigned row = 0; row < b.nRow; ++row)
575
54
    appendRowFromB(row);
576
12
577
12
  return result;
578
12
}
579
580
46
Optional<SmallVector<int64_t, 8>> Simplex::getSamplePointIfIntegral() const {
581
46
  // The tableau is empty, so no sample point exists.
582
46
  if (empty)
583
1
    return {};
584
45
585
45
  SmallVector<int64_t, 8> sample;
586
45
  // Push the sample value for each variable into the vector.
587
91
  for (const Unknown &u : var) {
588
91
    if (u.orientation == Orientation::Column) {
589
1
      // If the variable is in column position, its sample value is zero.
590
1
      sample.push_back(0);
591
90
    } else {
592
90
      // If the variable is in row position, its sample value is the entry in
593
90
      // the constant column divided by the entry in the common denominator
594
90
      // column. If this is not an integer, then the sample point is not
595
90
      // integral so we return None.
596
90
      if (tableau(u.pos, 1) % tableau(u.pos, 0) != 0)
597
28
        return {};
598
62
      sample.push_back(tableau(u.pos, 1) / tableau(u.pos, 0));
599
62
    }
600
91
  }
601
45
  return sample;
602
45
}
603
604
/// Given a simplex for a polytope, construct a new simplex whose variables are
605
/// identified with a pair of points (x, y) in the original polytope. Supports
606
/// some operations needed for generalized basis reduction. In what follows,
607
/// <x, y> denotes the dot product of the vectors x and y.
608
///
609
/// This supports adding equality constraints <dir, x - y> == 0. It also
610
/// supports rolling back this addition, by maintaining a snapshot stack that
611
/// contains a snapshot of the Simplex's state for each equality, just before
612
/// that equality was added.
613
class GBRSimplex {
614
  using Orientation = Simplex::Orientation;
615
616
public:
617
  GBRSimplex(const Simplex &originalSimplex)
618
      : simplex(Simplex::makeProduct(originalSimplex, originalSimplex)),
619
12
        simplexConstraintOffset(simplex.numConstraints()) {}
620
621
  /// Add an equality <dir, x - y> == 0.
622
  /// First pushes a snapshot for the current simplex state to the stack so
623
  /// that this can be rolled back later.
624
78
  void addEqualityForDirection(ArrayRef<int64_t> dir) {
625
78
    assert(
626
78
        std::any_of(dir.begin(), dir.end(), [](int64_t x) { return x != 0; }) &&
627
78
        "Direction passed is the zero vector!");
628
78
    snapshotStack.push_back(simplex.getSnapshot());
629
78
    simplex.addEquality(getCoeffsForDirection(dir));
630
78
  }
631
632
  /// Compute max(<dir, x - y>) and save the dual variables for only the
633
  /// direction equalities to `dual`.
634
  Fraction computeWidthAndDuals(ArrayRef<int64_t> dir,
635
                                SmallVectorImpl<int64_t> &dual,
636
97
                                int64_t &dualDenom) {
637
97
    unsigned snap = simplex.getSnapshot();
638
97
    unsigned conIndex = simplex.addRow(getCoeffsForDirection(dir));
639
97
    unsigned row = simplex.con[conIndex].pos;
640
97
    Optional<Fraction> maybeWidth =
641
97
        simplex.computeRowOptimum(Simplex::Direction::Up, row);
642
97
    assert(maybeWidth.hasValue() && "Width should not be unbounded!");
643
97
    dualDenom = simplex.tableau(row, 0);
644
97
    dual.clear();
645
97
    // The increment is i += 2 because equalities are added as two inequalities,
646
97
    // one positive and one negative. Each iteration processes one equality.
647
191
    for (unsigned i = simplexConstraintOffset; i < conIndex; i += 2) {
648
94
      // The dual variable is the negative of the coefficient of the new row
649
94
      // in the column of the constraint, if the constraint is in a column.
650
94
      // Note that the second inequality for the equality is negated.
651
94
      //
652
94
      // We want the dual for the original equality. If the positive inequality
653
94
      // is in column position, the negative of its row coefficient is the
654
94
      // desired dual. If the negative inequality is in column position, its row
655
94
      // coefficient is the desired dual. (its coefficients are already the
656
94
      // negated coefficients of the original equality, so we don't need to
657
94
      // negate it now.)
658
94
      //
659
94
      // If neither are in column position, we move the negated inequality to
660
94
      // column position. Since the inequality must have sample value zero
661
94
      // (since it corresponds to an equality), we are free to pivot with
662
94
      // any column. Since both the unknowns have sample value before and after
663
94
      // pivoting, no other sample values will change and the tableau will
664
94
      // remain consistent. To pivot, we just need to find a column that has a
665
94
      // non-zero coefficient in this row. There must be one since otherwise the
666
94
      // equality would be 0 == 0, which should never be passed to
667
94
      // addEqualityForDirection.
668
94
      //
669
94
      // After finding a column, we pivot with the column, after which we can
670
94
      // get the dual from the inequality in column position as explained above.
671
94
      if (simplex.con[i].orientation == Orientation::Column) {
672
32
        dual.push_back(-simplex.tableau(row, simplex.con[i].pos));
673
62
      } else {
674
62
        if (simplex.con[i + 1].orientation == Orientation::Row) {
675
10
          unsigned ineqRow = simplex.con[i + 1].pos;
676
10
          // Since it is an equality, the the sample value must be zero.
677
10
          assert(simplex.tableau(ineqRow, 1) == 0 &&
678
10
                 "Equality's sample value must be zero.");
679
16
          for (unsigned col = 2; col < simplex.nCol; ++col) {
680
16
            if (simplex.tableau(ineqRow, col) != 0) {
681
10
              simplex.pivot(ineqRow, col);
682
10
              break;
683
10
            }
684
16
          }
685
10
          assert(simplex.con[i + 1].orientation == Orientation::Column &&
686
10
                 "No pivot found. Equality has all-zeros row in tableau!");
687
10
        }
688
62
        dual.push_back(simplex.tableau(row, simplex.con[i + 1].pos));
689
62
      }
690
94
    }
691
97
    simplex.rollback(snap);
692
97
    return *maybeWidth;
693
97
  }
694
695
  /// Remove the last equality that was added through addEqualityForDirection.
696
  ///
697
  /// We do this by rolling back to the snapshot at the top of the stack, which
698
  /// should be a snapshot taken just before the last equality was added.
699
56
  void removeLastEquality() {
700
56
    assert(!snapshotStack.empty() && "Snapshot stack is empty!");
701
56
    simplex.rollback(snapshotStack.back());
702
56
    snapshotStack.pop_back();
703
56
  }
704
705
private:
706
  // Returns coefficients for the expression <dir, x - y>.
707
175
  SmallVector<int64_t, 8> getCoeffsForDirection(ArrayRef<int64_t> dir) {
708
175
    assert(2 * dir.size() == simplex.numVariables() &&
709
175
           "Direction vector has wrong dimensionality");
710
175
    SmallVector<int64_t, 8> coeffs(dir.begin(), dir.end());
711
175
    for (int64_t coeff : dir)
712
546
      coeffs.emplace_back(-coeff);
713
175
    coeffs.emplace_back(0); // constant term
714
175
    return coeffs;
715
175
  }
716
717
  Simplex simplex;
718
  // The first index of the equality constraints, the index immediately after
719
  // the last constraint in the initial product simplex.
720
  unsigned simplexConstraintOffset;
721
  // A stack of snapshots, used for rolling back.
722
  SmallVector<unsigned, 8> snapshotStack;
723
};
724
725
/// Reduce the basis to try and find a direction in which the polytope is
726
/// "thin". This only works for bounded polytopes.
727
///
728
/// This is an implementation of the algorithm described in the paper
729
/// "An Implementation of Generalized Basis Reduction for Integer Programming"
730
/// by W. Cook, T. Rutherford, H. E. Scarf, D. Shallcross.
731
///
732
/// Let b_{level}, b_{level + 1}, ... b_n be the current basis.
733
/// Let width_i(v) = max <v, x - y> where x and y are points in the original
734
/// polytope such that <b_j, x - y> = 0 is satisfied for all level <= j < i.
735
///
736
/// In every iteration, we first replace b_{i+1} with b_{i+1} + u*b_i, where u
737
/// is the integer such that width_i(b_{i+1} + u*b_i) is minimized. Let dual_i
738
/// be the dual variable associated with the constraint <b_i, x - y> = 0 when
739
/// computing width_{i+1}(b_{i+1}). It can be shown that dual_i is the
740
/// minimizing value of u, if it were allowed to be fractional. Due to
741
/// convexity, the minimizing integer value is either floor(dual_i) or
742
/// ceil(dual_i), so we just need to check which of these gives a lower
743
/// width_{i+1} value. If dual_i turned out to be an integer, then u = dual_i.
744
///
745
/// Now if width_i(b_{i+1}) < 0.75 * width_i(b_i), we swap b_i and (the new)
746
/// b_{i + 1} and decrement i (unless i = level, in which case we stay at the
747
/// same i). Otherwise, we increment i.
748
///
749
/// We keep f values and duals cached and invalidate them when necessary.
750
/// Whenever possible, we use them instead of recomputing them. We implement the
751
/// algorithm as follows.
752
///
753
/// In an iteration at i we need to compute:
754
///   a) width_i(b_{i + 1})
755
///   b) width_i(b_i)
756
///   c) the integer u that minimizes width_i(b_{i + 1} + u*b_i)
757
///
758
/// If width_i(b_i) is not already cached, we compute it.
759
///
760
/// If the duals are not already cached, we compute width_{i+1}(b_{i+1}) and
761
/// store the duals from this computation.
762
///
763
/// We call updateBasisWithUAndGetFCandidate, which finds the minimizing value
764
/// of u as explained before, caches the duals from this computation, sets
765
/// b_{i+1} to b_{i+1} + u*b_i, and returns the new value of width_i(b_{i+1}).
766
///
767
/// Now if width_i(b_{i+1}) < 0.75 * width_i(b_i), we swap b_i and b_{i+1} and
768
/// decrement i, resulting in the basis
769
/// ... b_{i - 1}, b_{i + 1} + u*b_i, b_i, b_{i+2}, ...
770
/// with corresponding f values
771
/// ... width_{i-1}(b_{i-1}), width_i(b_{i+1} + u*b_i), width_{i+1}(b_i), ...
772
/// The values up to i - 1 remain unchanged. We have just gotten the middle
773
/// value from updateBasisWithUAndGetFCandidate, so we can update that in the
774
/// cache. The value at width_{i+1}(b_i) is unknown, so we evict this value from
775
/// the cache. The iteration after decrementing needs exactly the duals from the
776
/// computation of width_i(b_{i + 1} + u*b_i), so we keep these in the cache.
777
///
778
/// When incrementing i, no cached f values get invalidated. However, the cached
779
/// duals do get invalidated as the duals for the higher levels are different.
780
13
void Simplex::reduceBasis(Matrix &basis, unsigned level) {
781
13
  const Fraction epsilon(3, 4);
782
13
783
13
  if (level == basis.getNumRows() - 1)
784
1
    return;
785
12
786
12
  GBRSimplex gbrSimplex(*this);
787
12
  SmallVector<Fraction, 8> width;
788
12
  SmallVector<int64_t, 8> dual;
789
12
  int64_t dualDenom;
790
12
791
12
  // Finds the value of u that minimizes width_i(b_{i+1} + u*b_i), caches the
792
12
  // duals from this computation, sets b_{i+1} to b_{i+1} + u*b_i, and returns
793
12
  // the new value of width_i(b_{i+1}).
794
12
  //
795
12
  // If dual_i is not an integer, the minimizing value must be either
796
12
  // floor(dual_i) or ceil(dual_i). We compute the expression for both and
797
12
  // choose the minimizing value.
798
12
  //
799
12
  // If dual_i is an integer, we don't need to perform these computations. We
800
12
  // know that in this case,
801
12
  //   a) u = dual_i.
802
12
  //   b) one can show that dual_j for j < i are the same duals we would have
803
12
  //      gotten from computing width_i(b_{i + 1} + u*b_i), so the correct duals
804
12
  //      are the ones already in the cache.
805
12
  //   c) width_i(b_{i+1} + u*b_i) = min_{alpha} width_i(b_{i+1} + alpha * b_i),
806
12
  //   which
807
12
  //      one can show is equal to width_{i+1}(b_{i+1}). The latter value must
808
12
  //      be in the cache, so we get it from there and return it.
809
56
  auto updateBasisWithUAndGetFCandidate = [&](unsigned i) -> Fraction {
810
56
    assert(i < level + dual.size() && "dual_i is not known!");
811
56
812
56
    int64_t u = floorDiv(dual[i - level], dualDenom);
813
56
    basis.addToRow(i, i + 1, u);
814
56
    if (dual[i - level] % dualDenom != 0) {
815
20
      SmallVector<int64_t, 8> candidateDual[2];
816
20
      int64_t candidateDualDenom[2];
817
20
      Fraction widthI[2];
818
20
819
20
      // Initially u is floor(dual) and basis reflects this.
820
20
      widthI[0] = gbrSimplex.computeWidthAndDuals(
821
20
          basis.getRow(i + 1), candidateDual[0], candidateDualDenom[0]);
822
20
823
20
      // Now try ceil(dual), i.e. floor(dual) + 1.
824
20
      ++u;
825
20
      basis.addToRow(i, i + 1, 1);
826
20
      widthI[1] = gbrSimplex.computeWidthAndDuals(
827
20
          basis.getRow(i + 1), candidateDual[1], candidateDualDenom[1]);
828
20
829
20
      unsigned j = widthI[0] < widthI[1] ? 0 : 1;
830
20
      if (j == 0)
831
5
        // Subtract 1 to go from u = ceil(dual) back to floor(dual).
832
5
        basis.addToRow(i, i + 1, -1);
833
20
      dual = std::move(candidateDual[j]);
834
20
      dualDenom = candidateDualDenom[j];
835
20
      return widthI[j];
836
20
    }
837
36
    assert(i + 1 - level < width.size() && "width_{i+1} wasn't saved");
838
36
    // When dual minimizes f_i(b_{i+1} + dual*b_i), this is equal to
839
36
    // width_{i+1}(b_{i+1}).
840
36
    return width[i + 1 - level];
841
36
  };
842
12
843
12
  // In the ith iteration of the loop, gbrSimplex has constraints for directions
844
12
  // from `level` to i - 1.
845
12
  unsigned i = level;
846
68
  while (i < basis.getNumRows() - 1) {
847
56
    if (i >= level + width.size()) {
848
12
      // We don't even know the value of f_i(b_i), so let's find that first.
849
12
      // We have to do this first since later we assume that width already
850
12
      // contains values up to and including i.
851
12
852
12
      assert((i == 0 || i - 1 < level + width.size()) &&
853
12
             "We are at level i but we don't know the value of width_{i-1}");
854
12
855
12
      // We don't actually use these duals at all, but it doesn't matter
856
12
      // because this case should only occur when i is level, and there are no
857
12
      // duals in that case anyway.
858
12
      assert(i == level && "This case should only occur when i == level");
859
12
      width.push_back(
860
12
          gbrSimplex.computeWidthAndDuals(basis.getRow(i), dual, dualDenom));
861
12
    }
862
56
863
56
    if (i >= level + dual.size()) {
864
45
      assert(i + 1 >= level + width.size() &&
865
45
             "We don't know dual_i but we know "
866
45
             "width_{i+1}");
867
45
      // We don't know dual for our level, so let's find it.
868
45
      gbrSimplex.addEqualityForDirection(basis.getRow(i));
869
45
      width.push_back(gbrSimplex.computeWidthAndDuals(basis.getRow(i + 1), dual,
870
45
                                                      dualDenom));
871
45
      gbrSimplex.removeLastEquality();
872
45
    }
873
56
874
56
    // width_i(b_{i+1} + u*b_i)
875
56
    Fraction widthICandidate = updateBasisWithUAndGetFCandidate(i);
876
56
    if (widthICandidate < epsilon * width[i - level]) {
877
23
      basis.swapRows(i, i + 1);
878
23
      width[i - level] = widthICandidate;
879
23
      // The values of width_{i+1}(b_{i+1}) and higher may change after the
880
23
      // swap, so we remove the cached values here.
881
23
      width.resize(i - level + 1);
882
23
      if (i == level) {
883
12
        dual.clear();
884
12
        continue;
885
12
      }
886
11
887
11
      gbrSimplex.removeLastEquality();
888
11
      i--;
889
11
      continue;
890
11
    }
891
33
892
33
    // Invalidate duals since the higher level needs to recompute its own duals.
893
33
    dual.clear();
894
33
    gbrSimplex.addEqualityForDirection(basis.getRow(i));
895
33
    i++;
896
33
  }
897
12
}
898
899
/// Search for an integer sample point using a branch and bound algorithm.
900
///
901
/// Each row in the basis matrix is a vector, and the set of basis vectors
902
/// should span the space. Initially this is the identity matrix,
903
/// i.e., the basis vectors are just the variables.
904
///
905
/// In every level, a value is assigned to the level-th basis vector, as
906
/// follows. Compute the minimum and maximum rational values of this direction.
907
/// If only one integer point lies in this range, constrain the variable to
908
/// have this value and recurse to the next variable.
909
///
910
/// If the range has multiple values, perform generalized basis reduction via
911
/// reduceBasis and then compute the bounds again. Now we try constraining
912
/// this direction in the first value in this range and "recurse" to the next
913
/// level. If we fail to find a sample, we try assigning the direction the next
914
/// value in this range, and so on.
915
///
916
/// If no integer sample is found from any of the assignments, or if the range
917
/// contains no integer value, then of course the polytope is empty for the
918
/// current assignment of the values in previous levels, so we return to
919
/// the previous level.
920
///
921
/// If we reach the last level where all the variables have been assigned values
922
/// already, then we simply return the current sample point if it is integral,
923
/// and go back to the previous level otherwise.
924
///
925
/// To avoid potentially arbitrarily large recursion depths leading to stack
926
/// overflows, this algorithm is implemented iteratively.
927
30
Optional<SmallVector<int64_t, 8>> Simplex::findIntegerSample() {
928
30
  if (empty)
929
1
    return {};
930
29
931
29
  unsigned nDims = var.size();
932
29
  Matrix basis = Matrix::identity(nDims);
933
29
934
29
  unsigned level = 0;
935
29
  // The snapshot just before constraining a direction to a value at each level.
936
29
  SmallVector<unsigned, 8> snapshotStack;
937
29
  // The maximum value in the range of the direction for each level.
938
29
  SmallVector<int64_t, 8> upperBoundStack;
939
29
  // The next value to try constraining the basis vector to at each level.
940
29
  SmallVector<int64_t, 8> nextValueStack;
941
29
942
29
  snapshotStack.reserve(basis.getNumRows());
943
29
  upperBoundStack.reserve(basis.getNumRows());
944
29
  nextValueStack.reserve(basis.getNumRows());
945
61
  while (level != -1u) {
946
47
    if (level == basis.getNumRows()) {
947
4
      // We've assigned values to all variables. Return if we have a sample,
948
4
      // or go back up to the previous level otherwise.
949
4
      if (auto maybeSample = getSamplePointIfIntegral())
950
4
        return maybeSample;
951
0
      level--;
952
0
      continue;
953
0
    }
954
43
955
43
    if (level >= upperBoundStack.size()) {
956
38
      // We haven't populated the stack values for this level yet, so we have
957
38
      // just come down a level ("recursed"). Find the lower and upper bounds.
958
38
      // If there is more than one integer point in the range, perform
959
38
      // generalized basis reduction.
960
38
      SmallVector<int64_t, 8> basisCoeffs(basis.getRow(level).begin(),
961
38
                                          basis.getRow(level).end());
962
38
      basisCoeffs.push_back(0);
963
38
964
38
      int64_t minRoundedUp, maxRoundedDown;
965
38
      std::tie(minRoundedUp, maxRoundedDown) =
966
38
          computeIntegerBounds(basisCoeffs);
967
38
968
38
      // Heuristic: if the sample point is integral at this point, just return
969
38
      // it.
970
38
      if (auto maybeSample = getSamplePointIfIntegral())
971
11
        return *maybeSample;
972
27
973
27
      if (minRoundedUp < maxRoundedDown) {
974
13
        reduceBasis(basis, level);
975
13
        basisCoeffs = SmallVector<int64_t, 8>(basis.getRow(level).begin(),
976
13
                                              basis.getRow(level).end());
977
13
        basisCoeffs.push_back(0);
978
13
        std::tie(minRoundedUp, maxRoundedDown) =
979
13
            computeIntegerBounds(basisCoeffs);
980
13
      }
981
27
982
27
      snapshotStack.push_back(getSnapshot());
983
27
      // The smallest value in the range is the next value to try.
984
27
      nextValueStack.push_back(minRoundedUp);
985
27
      upperBoundStack.push_back(maxRoundedDown);
986
27
    }
987
43
988
43
    assert((snapshotStack.size() - 1 == level &&
989
32
            nextValueStack.size() - 1 == level &&
990
32
            upperBoundStack.size() - 1 == level) &&
991
32
           "Mismatched variable stack sizes!");
992
32
993
32
    // Whether we "recursed" or "returned" from a lower level, we rollback
994
32
    // to the snapshot of the starting state at this level. (in the "recursed"
995
32
    // case this has no effect)
996
32
    rollback(snapshotStack.back());
997
32
    int64_t nextValue = nextValueStack.back();
998
32
    nextValueStack.back()++;
999
32
    if (nextValue > upperBoundStack.back()) {
1000
19
      // We have exhausted the range and found no solution. Pop the stack and
1001
19
      // return up a level.
1002
19
      snapshotStack.pop_back();
1003
19
      nextValueStack.pop_back();
1004
19
      upperBoundStack.pop_back();
1005
19
      level--;
1006
19
      continue;
1007
19
    }
1008
13
1009
13
    // Try the next value in the range and "recurse" into the next level.
1010
13
    SmallVector<int64_t, 8> basisCoeffs(basis.getRow(level).begin(),
1011
13
                                        basis.getRow(level).end());
1012
13
    basisCoeffs.push_back(-nextValue);
1013
13
    addEquality(basisCoeffs);
1014
13
    level++;
1015
13
  }
1016
29
1017
29
  return {};
1018
29
}
1019
1020
/// Compute the minimum and maximum integer values the expression can take. We
1021
/// compute each separately.
1022
std::pair<int64_t, int64_t>
1023
51
Simplex::computeIntegerBounds(ArrayRef<int64_t> coeffs) {
1024
51
  int64_t minRoundedUp;
1025
51
  if (Optional<Fraction> maybeMin =
1026
51
          computeOptimum(Simplex::Direction::Down, coeffs))
1027
51
    minRoundedUp = ceil(*maybeMin);
1028
51
  else
1029
51
    llvm_unreachable("Tableau should not be unbounded");
1030
51
1031
51
  int64_t maxRoundedDown;
1032
51
  if (Optional<Fraction> maybeMax =
1033
51
          computeOptimum(Simplex::Direction::Up, coeffs))
1034
51
    maxRoundedDown = floor(*maybeMax);
1035
51
  else
1036
51
    llvm_unreachable("Tableau should not be unbounded");
1037
51
1038
51
  return {minRoundedUp, maxRoundedDown};
1039
51
}
1040
1041
0
void Simplex::print(raw_ostream &os) const {
1042
0
  os << "rows = " << nRow << ", columns = " << nCol << "\n";
1043
0
  if (empty)
1044
0
    os << "Simplex marked empty!\n";
1045
0
  os << "var: ";
1046
0
  for (unsigned i = 0; i < var.size(); ++i) {
1047
0
    if (i > 0)
1048
0
      os << ", ";
1049
0
    var[i].print(os);
1050
0
  }
1051
0
  os << "\ncon: ";
1052
0
  for (unsigned i = 0; i < con.size(); ++i) {
1053
0
    if (i > 0)
1054
0
      os << ", ";
1055
0
    con[i].print(os);
1056
0
  }
1057
0
  os << '\n';
1058
0
  for (unsigned row = 0; row < nRow; ++row) {
1059
0
    if (row > 0)
1060
0
      os << ", ";
1061
0
    os << "r" << row << ": " << rowUnknown[row];
1062
0
  }
1063
0
  os << '\n';
1064
0
  os << "c0: denom, c1: const";
1065
0
  for (unsigned col = 2; col < nCol; ++col)
1066
0
    os << ", c" << col << ": " << colUnknown[col];
1067
0
  os << '\n';
1068
0
  for (unsigned row = 0; row < nRow; ++row) {
1069
0
    for (unsigned col = 0; col < nCol; ++col)
1070
0
      os << tableau(row, col) << '\t';
1071
0
    os << '\n';
1072
0
  }
1073
0
  os << '\n';
1074
0
}
1075
1076
0
void Simplex::dump() const { print(llvm::errs()); }
1077
1078
} // namespace mlir