/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 |