Coverage Report

Created: 2020-06-26 05:44

/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