/home/arjun/llvm-project/mlir/include/mlir/Analysis/Presburger/Simplex.h
Line | Count | Source (jump to first uncovered line) |
1 | | //===- Simplex.h - MLIR Simplex Class ---------------------------*- C++ -*-===// |
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 | | // Functionality to perform analysis on FlatAffineConstraints. In particular, |
10 | | // support for performing emptiness checks. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #ifndef MLIR_ANALYSIS_PRESBURGER_SIMPLEX_H |
15 | | #define MLIR_ANALYSIS_PRESBURGER_SIMPLEX_H |
16 | | |
17 | | #include "mlir/Analysis/AffineStructures.h" |
18 | | #include "mlir/Analysis/Presburger/Fraction.h" |
19 | | #include "mlir/Analysis/Presburger/Matrix.h" |
20 | | #include "mlir/Support/LogicalResult.h" |
21 | | #include "llvm/ADT/ArrayRef.h" |
22 | | #include "llvm/ADT/Optional.h" |
23 | | #include "llvm/ADT/SmallVector.h" |
24 | | #include "llvm/Support/raw_ostream.h" |
25 | | |
26 | | namespace mlir { |
27 | | |
28 | | class GBRSimplex; |
29 | | |
30 | | /// This class implements a version of the Simplex and Generalized Basis |
31 | | /// Reduction algorithms, which can perform analysis of integer sets with affine |
32 | | /// inequalities and equalities. A Simplex can be constructed |
33 | | /// by specifying the dimensionality of the set. It supports adding affine |
34 | | /// inequalities and equalities, and can perform emptiness checks, i.e., it can |
35 | | /// find a solution to the set of constraints if one exists, or say that the |
36 | | /// set is empty if no solution exists. Currently, this only works for bounded |
37 | | /// sets. Simplex can also be constructed from a FlatAffineConstraints object. |
38 | | /// |
39 | | /// The implementation of this Simplex class, other than the functionality |
40 | | /// for sampling, is based on the paper |
41 | | /// "Simplify: A Theorem Prover for Program Checking" |
42 | | /// by D. Detlefs, G. Nelson, J. B. Saxe. |
43 | | /// |
44 | | /// We define variables, constraints, and unknowns. Consider the example of a |
45 | | /// two-dimensional set defined by 1 + 2x + 3y >= 0 and 2x - 3y >= 0. Here, |
46 | | /// x, y, are variables while 1 + 2x + 3y >= 0, 2x - 3y >= 0 are |
47 | | /// constraints. Unknowns are either variables or constraints, i.e., x, y, |
48 | | /// 1 + 2x + 3y >= 0, 2x - 3y >= 0 are all unknowns. |
49 | | /// |
50 | | /// The implementation involves a matrix called a tableau, which can be thought |
51 | | /// of as a 2D matrix of rational numbers having number of rows equal to the |
52 | | /// number of constraints and number of columns equal to one plus the number of |
53 | | /// variables. In our implementation, instead of storing rational numbers, we |
54 | | /// store a common denominator for each row, so it is in fact a matrix of |
55 | | /// integers with number of rows equal to number of constraints and number of |
56 | | /// columns equal to _two_ plus the number of variables. For example, instead of |
57 | | /// storing a row of three rationals [1/2, 2/3, 3], we would store [6, 3, 4, 18] |
58 | | /// since 3/6 = 1/2, 4/6 = 2/3, and 18/6 = 3. |
59 | | /// |
60 | | /// Every row and column except the first and second columns is associated with |
61 | | /// an unknown and every unknown is associated with a row or column. The second |
62 | | /// column represents the constant, explained in more detail below. An unknown |
63 | | /// associated with a row or column is said to be in row or column position |
64 | | /// respectively. |
65 | | /// |
66 | | /// The vectors var and con store information about the variables and |
67 | | /// constraints respectively, namely, whether they are in row or column |
68 | | /// position, which row or column they are associated with, and whether they |
69 | | /// correspond to a variable or a constraint. |
70 | | /// |
71 | | /// An unknown is addressed by its index. If the index i is non-negative, then |
72 | | /// the variable var[i] is being addressed. If the index i is negative, then |
73 | | /// the constraint con[~i] is being addressed. Effectively this maps |
74 | | /// 0 -> var[0], 1 -> var[1], -1 -> con[0], -2 -> con[1], etc. rowUnknown[r] and |
75 | | /// colUnknown[c] are the indexes of the unknowns associated with row r and |
76 | | /// column c, respectively. |
77 | | /// |
78 | | /// The unknowns in column position are together called the basis. Initially the |
79 | | /// basis is the set of variables -- in our example above, the initial basis is |
80 | | /// x, y. |
81 | | /// |
82 | | /// The unknowns in row position are represented in terms of the basis unknowns. |
83 | | /// If the basis unknowns are u_1, u_2, ... u_m, and a row in the tableau is |
84 | | /// d, c, a_1, a_2, ... a_m, this representats the unknown for that row as |
85 | | /// (c + a_1*u_1 + a_2*u_2 + ... + a_m*u_m)/d. In our running example, if the |
86 | | /// basis is the initial basis of x, y, then the constraint 1 + 2x + 3y >= 0 |
87 | | /// would be represented by the row [1, 1, 2, 3]. |
88 | | /// |
89 | | /// The association of unknowns to rows and columns can be changed by a process |
90 | | /// called pivoting, where a row unknown and a column unknown exchange places |
91 | | /// and the remaining row variables' representation is changed accordingly |
92 | | /// by eliminating the old column unknown in favour of the new column unknown. |
93 | | /// If we had pivoted the column for x with the row for 2x - 3y >= 0, |
94 | | /// the new row for x would be [2, 1, 3] since x = (1*(2x - 3y) + 3*y)/2. |
95 | | /// See the documentation for the pivot member function for details. |
96 | | /// |
97 | | /// The association of unknowns to rows and columns is called the _tableau |
98 | | /// configuration_. The _sample value_ of an unknown in a particular tableau |
99 | | /// configuration is its value if all the column unknowns were set to zero. |
100 | | /// Concretely, for unknowns in column position the sample value is zero and |
101 | | /// for unknowns in row position the sample value is the constant term divided |
102 | | /// by the common denominator. |
103 | | /// |
104 | | /// The tableau configuration is called _consistent_ if the sample value of all |
105 | | /// restricted unknowns is non-negative. Initially there are no constraints, and |
106 | | /// the tableau is consistent. When a new constraint is added, its sample value |
107 | | /// in the current tableau configuration may be negative. In that case, we try |
108 | | /// to find a series of pivots to bring us to a consistent tableau |
109 | | /// configuration, i.e. we try to make the new constraint's sample value |
110 | | /// non-negative without making that of any other constraints negative. (See |
111 | | /// findPivot and findPivotRow for details.) If this is not possible, then the |
112 | | /// set of constraints is mutually contradictory and the tableau is marked |
113 | | /// _empty_, which means the set of constraints has no solution. |
114 | | /// |
115 | | /// This Simplex class also supports taking snapshots of the current state |
116 | | /// and rolling back to prior snapshots. This works by maintaing an undo log |
117 | | /// of operations. Snapshots are just pointers to a particular location in the |
118 | | /// log, and rolling back to a snapshot is done by reverting each log entry's |
119 | | /// operation from the end until we reach the snapshot's location. |
120 | | /// |
121 | | /// Finding an integer sample is done with the Generalized Basis Reduction |
122 | | /// algorithm. See the documentation for findIntegerSample and reduceBasis. |
123 | | class Simplex { |
124 | | public: |
125 | | enum class Direction { Up, Down }; |
126 | | |
127 | | Simplex() = delete; |
128 | | explicit Simplex(unsigned nVar); |
129 | | explicit Simplex(const FlatAffineConstraints &constraints); |
130 | | |
131 | | /// Returns true if the tableau is empty (has conflicting constraints), |
132 | | /// false otherwise. |
133 | | bool isEmpty() const; |
134 | | |
135 | | /// Add an inequality to the tableau. If coeffs is c_0, c_1, ... c_n, where n |
136 | | /// is the current number of variables, then the corresponding inequality is |
137 | | /// c_n + c_0*x_0 + c_1*x_1 + ... + c_{n-1}*x_{n-1} >= 0. |
138 | | void addInequality(ArrayRef<int64_t> coeffs); |
139 | | |
140 | | /// Returns the number of variables in the tableau. |
141 | | unsigned numVariables() const; |
142 | | |
143 | | /// Returns the number of constraints in the tableau. |
144 | | unsigned numConstraints() const; |
145 | | |
146 | | /// Add an equality to the tableau. If coeffs is c_0, c_1, ... c_n, where n |
147 | | /// is the current number of variables, then the corresponding equality is |
148 | | /// c_n + c_0*x_0 + c_1*x_1 + ... + c_{n-1}*x_{n-1} == 0. |
149 | | void addEquality(ArrayRef<int64_t> coeffs); |
150 | | |
151 | | /// Mark the tableau as being empty. |
152 | | void markEmpty(); |
153 | | |
154 | | /// Get a snapshot of the current state. This is used for rolling back. |
155 | | unsigned getSnapshot() const; |
156 | | |
157 | | /// Rollback to a snapshot. This invalidates all later snapshots. |
158 | | void rollback(unsigned snapshot); |
159 | | |
160 | | /// Compute the maximum or minimum value of the given row, depending on |
161 | | /// direction. |
162 | | /// |
163 | | /// Returns a (num, den) pair denoting the optimum, or None if no |
164 | | /// optimum exists, i.e., if the expression is unbounded in this direction. |
165 | | Optional<Fraction> computeRowOptimum(Direction direction, unsigned row); |
166 | | |
167 | | /// Compute the maximum or minimum value of the given expression, depending on |
168 | | /// direction. |
169 | | /// |
170 | | /// Returns a (num, den) pair denoting the optimum, or a null value if no |
171 | | /// optimum exists, i.e., if the expression is unbounded in this direction. |
172 | | Optional<Fraction> computeOptimum(Direction direction, |
173 | | ArrayRef<int64_t> coeffs); |
174 | | |
175 | | /// Returns a (min, max) pair denoting the minimum and maximum integer values |
176 | | /// of the given expression. |
177 | | std::pair<int64_t, int64_t> computeIntegerBounds(ArrayRef<int64_t> coeffs); |
178 | | |
179 | | /// Returns true if the polytope is unbounded, i.e., extends to infinity in |
180 | | /// some direction. Otherwise, returns false. |
181 | | bool isUnbounded(); |
182 | | |
183 | | /// Make a tableau to represent a pair of points in the given tableaus, one in |
184 | | /// tableau A and one in B. |
185 | | static Simplex makeProduct(const Simplex &a, const Simplex &b); |
186 | | |
187 | | /// Returns the current sample point if it is integral. Otherwise, returns an |
188 | | /// None. |
189 | | Optional<SmallVector<int64_t, 8>> getSamplePointIfIntegral() const; |
190 | | |
191 | | /// Returns an integer sample point if one exists, or None |
192 | | /// otherwise. This should only be called for bounded sets. |
193 | | Optional<SmallVector<int64_t, 8>> findIntegerSample(); |
194 | | |
195 | | /// Print the tableau's internal state. |
196 | | void print(raw_ostream &os) const; |
197 | | void dump() const; |
198 | | |
199 | | private: |
200 | | friend class GBRSimplex; |
201 | | |
202 | | enum class Orientation { Row, Column }; |
203 | | |
204 | | /// An Unknown is either a variable or a constraint. It is always associated |
205 | | /// with either a row or column. Whether it's a row or a column is specified |
206 | | /// by the orientation and pos identifies the specific row or column it is |
207 | | /// associated with. If the unknown is restricted, then it has a |
208 | | /// non-negativity constraint associated with it, i.e., its sample value must |
209 | | /// always be non-negative and if it cannot be made non-negative without |
210 | | /// violating other constraints, the tableau is empty. |
211 | | struct Unknown { |
212 | | Unknown(Orientation oOrientation, bool oRestricted, unsigned oPos) |
213 | 858 | : pos(oPos), orientation(oOrientation), restricted(oRestricted) {} |
214 | | unsigned pos; |
215 | | Orientation orientation; |
216 | | bool restricted : 1; |
217 | | |
218 | 0 | void print(raw_ostream &os) const { |
219 | 0 | os << (orientation == Orientation::Row ? "r" : "c"); |
220 | 0 | os << pos; |
221 | 0 | if (restricted) |
222 | 0 | os << " [>=0]"; |
223 | 0 | } |
224 | | }; |
225 | | |
226 | | struct Pivot { |
227 | | unsigned row, column; |
228 | | }; |
229 | | |
230 | | /// Find a pivot to change the sample value of row in the specified |
231 | | /// direction. The returned pivot row will be row if and only |
232 | | /// if the unknown is unbounded in the specified direction. |
233 | | /// |
234 | | /// Returns a (row, col) pair denoting a pivot, or an empty Optional if |
235 | | /// no valid pivot exists. |
236 | | Optional<Pivot> findPivot(int row, Direction direction) const; |
237 | | |
238 | | /// Swap the row with the column in the tableau's data structures but not the |
239 | | /// tableau itself. This is used by pivot. |
240 | | void swapRowWithCol(unsigned row, unsigned col); |
241 | | |
242 | | /// Pivot the row with the column. |
243 | | void pivot(unsigned row, unsigned col); |
244 | | void pivot(Pivot pair); |
245 | | |
246 | | /// Returns the unknown associated with index. |
247 | | const Unknown &unknownFromIndex(int index) const; |
248 | | /// Returns the unknown associated with col. |
249 | | const Unknown &unknownFromColumn(unsigned col) const; |
250 | | /// Returns the unknown associated with row. |
251 | | const Unknown &unknownFromRow(unsigned row) const; |
252 | | /// Returns the unknown associated with index. |
253 | | Unknown &unknownFromIndex(int index); |
254 | | /// Returns the unknown associated with col. |
255 | | Unknown &unknownFromColumn(unsigned col); |
256 | | /// Returns the unknown associated with row. |
257 | | Unknown &unknownFromRow(unsigned row); |
258 | | |
259 | | /// Add a new row to the tableau and the associated data structures. |
260 | | unsigned addRow(ArrayRef<int64_t> coeffs); |
261 | | |
262 | | /// Normalize the given row by removing common factors between the numerator |
263 | | /// and the denominator. |
264 | | void normalizeRow(unsigned row); |
265 | | |
266 | | /// Swap the two rows in the tableau and associated data structures. |
267 | | void swapRows(unsigned i, unsigned j); |
268 | | |
269 | | /// Restore the unknown to a non-negative sample value. |
270 | | /// |
271 | | /// Returns true if the unknown was successfully restored to a non-negative |
272 | | /// sample value, false otherwise. |
273 | | LogicalResult restoreRow(Unknown &u); |
274 | | |
275 | | enum class UndoLogEntry { RemoveLastConstraint, UnmarkEmpty }; |
276 | | |
277 | | /// Undo the operation represented by the log entry. |
278 | | void undo(UndoLogEntry entry); |
279 | | |
280 | | /// Find a row that can be used to pivot the column in the specified |
281 | | /// direction. If skipRow is not null, then this row is excluded |
282 | | /// from consideration. The returned pivot will maintain all constraints |
283 | | /// except the column itself and skipRow, if it is set. (if these unknowns |
284 | | /// are restricted). |
285 | | /// |
286 | | /// Returns the row to pivot to, or an empty Optional if the column |
287 | | /// is unbounded in the specified direction. |
288 | | Optional<unsigned> findPivotRow(Optional<unsigned> skipRow, |
289 | | Direction direction, unsigned col) const; |
290 | | |
291 | | /// Reduce the given basis, starting at the specified level, using general |
292 | | /// basis reduction. |
293 | | void reduceBasis(Matrix &basis, unsigned level); |
294 | | |
295 | | /// The number of rows in the tableau. |
296 | | unsigned nRow; |
297 | | |
298 | | /// The number of columns in the tableau, including the common denominator |
299 | | /// and the constant column. |
300 | | unsigned nCol; |
301 | | |
302 | | /// The matrix representing the tableau. |
303 | | Matrix tableau; |
304 | | |
305 | | /// This is true if the tableau has been detected to be empty, false |
306 | | /// otherwise. |
307 | | bool empty; |
308 | | |
309 | | /// Holds a log of operations, used for rolling back to a previous state. |
310 | | SmallVector<UndoLogEntry, 8> undoLog; |
311 | | |
312 | | /// These hold the indexes of the unknown at a given row or column position. |
313 | | /// We keep these as signed integers since that makes it convenient to check |
314 | | /// if an index corresponds to a variable or a constraint by checking the |
315 | | /// sign. |
316 | | /// |
317 | | /// colUnknown is padded with two null indexes at the front since the first |
318 | | /// two columns don't correspond to any unknowns. |
319 | | SmallVector<int, 8> rowUnknown, colUnknown; |
320 | | |
321 | | /// These hold information about each unknown. |
322 | | SmallVector<Unknown, 8> con, var; |
323 | | }; |
324 | | |
325 | | } // namespace mlir |
326 | | |
327 | | #endif // MLIR_ANALYSIS_PRESBURGER_SIMPLEX_H |