MLIR 23.0.0git
Matrix.h
Go to the documentation of this file.
1//===- Matrix.h - MLIR Matrix 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// This is a simple 2D matrix class that supports reading, writing, resizing,
10// swapping rows, and swapping columns. It can hold integers (DynamicAPInt) or
11// rational numbers (Fraction).
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef MLIR_ANALYSIS_PRESBURGER_MATRIX_H
16#define MLIR_ANALYSIS_PRESBURGER_MATRIX_H
17
19#include "llvm/ADT/ArrayRef.h"
20#include "llvm/Support/raw_ostream.h"
21#include <cassert>
22
23namespace mlir {
24namespace presburger {
25using llvm::ArrayRef;
26using llvm::MutableArrayRef;
27using llvm::raw_ostream;
28using llvm::SmallVector;
29
30/// This is a class to represent a resizable matrix.
31///
32/// More columns and rows can be reserved than are currently used. The data is
33/// stored as a single 1D array, viewed as a 2D matrix with nRows rows and
34/// nReservedColumns columns, stored in row major form. Thus the element at
35/// (i, j) is stored at data[i*nReservedColumns + j]. The reserved but unused
36/// columns always have all zero values. The reserved rows are just reserved
37/// space in the underlying SmallVector's capacity.
38/// This class only works for the types DynamicAPInt and Fraction, since the
39/// method implementations are in the Matrix.cpp file. Only these two types have
40/// been explicitly instantiated there.
41template <typename T>
42class Matrix {
43 static_assert(std::is_same_v<T, DynamicAPInt> || std::is_same_v<T, Fraction>,
44 "T must be DynamicAPInt or Fraction.");
45
46public:
47 Matrix() = delete;
48
49 /// Construct a matrix with the specified number of rows and columns.
50 /// The number of reserved rows and columns will be at least the number
51 /// specified, and will always be sufficient to accomodate the number of rows
52 /// and columns specified.
53 ///
54 /// Initially, the entries are initialized to ero.
55 Matrix(unsigned rows, unsigned columns, unsigned reservedRows = 0,
56 unsigned reservedColumns = 0);
57
58 /// Return the identity matrix of the specified dimension.
59 static Matrix identity(unsigned dimension);
60
61 /// Access the element at the specified row and column.
62 T &at(unsigned row, unsigned column) {
63 assert(row < nRows && "Row outside of range");
64 assert(column < nColumns && "Column outside of range");
65 return data[row * nReservedColumns + column];
66 }
67
68 T at(unsigned row, unsigned column) const {
69 assert(row < nRows && "Row outside of range");
70 assert(column < nColumns && "Column outside of range");
71 return data[row * nReservedColumns + column];
72 }
73
74 T &operator()(unsigned row, unsigned column) { return at(row, column); }
75
76 T operator()(unsigned row, unsigned column) const { return at(row, column); }
77
78 bool operator==(const Matrix<T> &m) const;
79
80 /// Swap the given columns.
81 void swapColumns(unsigned column, unsigned otherColumn);
82
83 /// Swap the given rows.
84 void swapRows(unsigned row, unsigned otherRow);
85
86 unsigned getNumRows() const { return nRows; }
87
88 unsigned getNumColumns() const { return nColumns; }
89
90 /// Return the maximum number of rows/columns that can be added without
91 /// incurring a reallocation.
92 unsigned getNumReservedRows() const;
93 unsigned getNumReservedColumns() const { return nReservedColumns; }
94
95 /// Reserve enough space to resize to the specified number of rows without
96 /// reallocations.
97 void reserveRows(unsigned rows);
98
99 /// Get a [Mutable]ArrayRef corresponding to the specified row.
100 MutableArrayRef<T> getRow(unsigned row);
101 ArrayRef<T> getRow(unsigned row) const;
102
103 /// Set the specified row to `elems`.
104 void setRow(unsigned row, ArrayRef<T> elems);
105
106 /// Insert columns having positions pos, pos + 1, ... pos + count - 1.
107 /// Columns that were at positions 0 to pos - 1 will stay where they are;
108 /// columns that were at positions pos to nColumns - 1 will be pushed to the
109 /// right. pos should be at most nColumns.
110 void insertColumns(unsigned pos, unsigned count);
111 void insertColumn(unsigned pos);
112
113 /// Insert rows having positions pos, pos + 1, ... pos + count - 1.
114 /// Rows that were at positions 0 to pos - 1 will stay where they are;
115 /// rows that were at positions pos to nColumns - 1 will be pushed to the
116 /// right. pos should be at most nRows.
117 void insertRows(unsigned pos, unsigned count);
118 void insertRow(unsigned pos);
119
120 /// Remove the columns having positions pos, pos + 1, ... pos + count - 1.
121 /// Rows that were at positions 0 to pos - 1 will stay where they are;
122 /// columns that were at positions pos + count - 1 or later will be pushed to
123 /// the right. The columns to be deleted must be valid rows: pos + count - 1
124 /// must be at most nColumns - 1.
125 void removeColumns(unsigned pos, unsigned count);
126 void removeColumn(unsigned pos);
127
128 /// Remove the rows having positions pos, pos + 1, ... pos + count - 1.
129 /// Rows that were at positions 0 to pos - 1 will stay where they are;
130 /// rows that were at positions pos + count - 1 or later will be pushed to the
131 /// right. The rows to be deleted must be valid rows: pos + count - 1 must be
132 /// at most nRows - 1.
133 void removeRows(unsigned pos, unsigned count);
134 void removeRow(unsigned pos);
135
136 void copyRow(unsigned sourceRow, unsigned targetRow);
137
138 void fillRow(unsigned row, const T &value);
139 void fillRow(unsigned row, int64_t value) { fillRow(row, T(value)); }
140
141 /// Add `scale` multiples of the source row to the target row.
142 void addToRow(unsigned sourceRow, unsigned targetRow, const T &scale);
143 void addToRow(unsigned sourceRow, unsigned targetRow, int64_t scale) {
144 addToRow(sourceRow, targetRow, T(scale));
145 }
146 /// Add `scale` multiples of the rowVec row to the specified row.
147 void addToRow(unsigned row, ArrayRef<T> rowVec, const T &scale);
148
149 /// Multiply the specified row by a factor of `scale`.
150 void scaleRow(unsigned row, const T &scale);
151
152 /// Add `scale` multiples of the source column to the target column.
153 void addToColumn(unsigned sourceColumn, unsigned targetColumn,
154 const T &scale);
155 void addToColumn(unsigned sourceColumn, unsigned targetColumn,
156 int64_t scale) {
157 addToColumn(sourceColumn, targetColumn, T(scale));
158 }
159
160 /// Negate the specified column.
161 void negateColumn(unsigned column);
162
163 /// Negate the specified row.
164 void negateRow(unsigned row);
165
166 /// Negate the entire matrix.
167 void negateMatrix();
168
169 /// The given vector is interpreted as a row vector v. Post-multiply v with
170 /// this matrix, say M, and return vM.
172
173 /// The given vector is interpreted as a column vector v. Pre-multiply v with
174 /// this matrix, say M, and return Mv.
176
177 /// Resize the matrix to the specified dimensions. If a dimension is smaller,
178 /// the values are truncated; if it is bigger, the new values are initialized
179 /// to zero.
180 ///
181 /// Due to the representation of the matrix, resizing vertically (adding rows)
182 /// is less expensive than increasing the number of columns beyond
183 /// nReservedColumns.
184 void resize(unsigned newNRows, unsigned newNColumns);
185 void resizeHorizontally(unsigned newNColumns);
186 void resizeVertically(unsigned newNRows);
187
188 /// Add an extra row at the bottom of the matrix and return its position.
189 unsigned appendExtraRow();
190 /// Same as above, but copy the given elements into the row. The length of
191 /// `elems` must be equal to the number of columns.
192 unsigned appendExtraRow(ArrayRef<T> elems);
193
194 // Transpose the matrix without modifying it.
195 Matrix<T> transpose() const;
196
197 // Copy the cells in the intersection of
198 // the rows between `fromRows` and `toRows` and
199 // the columns between `fromColumns` and `toColumns`, both inclusive.
200 Matrix<T> getSubMatrix(unsigned fromRow, unsigned toRow, unsigned fromColumn,
201 unsigned toColumn) const;
202
203 /// Split the rows of a matrix into two matrices according to which bits are
204 /// 1 and which are 0 in a given bitset.
205 ///
206 /// The first matrix returned has the rows corresponding to 1 and the second
207 /// corresponding to 2.
208 std::pair<Matrix<T>, Matrix<T>> splitByBitset(ArrayRef<int> indicator);
209
210 /// Print the matrix.
211 void print(raw_ostream &os) const;
212 void dump() const;
213
214 /// Return whether the Matrix is in a consistent state with all its
215 /// invariants satisfied.
216 bool hasConsistentState() const;
217
218 /// Move the columns in the source range [srcPos, srcPos + num) to the
219 /// specified destination [dstPos, dstPos + num), while moving the columns
220 /// adjacent to the source range to the left/right of the shifted columns.
221 ///
222 /// When moving the source columns right (i.e. dstPos > srcPos), columns that
223 /// were at positions [0, srcPos) and [dstPos + num, nCols) will stay where
224 /// they are; columns that were at positions [srcPos, srcPos + num) will be
225 /// moved to [dstPos, dstPos + num); and columns that were at positions
226 /// [srcPos + num, dstPos + num) will be moved to [srcPos, dstPos).
227 /// Equivalently, the columns [srcPos + num, dstPos + num) are interchanged
228 /// with [srcPos, srcPos + num).
229 /// For example, if m = |0 1 2 3 4 5| then:
230 /// m.moveColumns(1, 3, 2) will result in m = |0 4 1 2 3 5|; or
231 /// m.moveColumns(1, 2, 4) will result in m = |0 3 4 5 1 2|.
232 ///
233 /// The left shift operation (i.e. dstPos < srcPos) works in a similar way.
234 void moveColumns(unsigned srcPos, unsigned num, unsigned dstPos);
235
236 /// Returns the matrix right-multiplied with `other`.
237 Matrix<T> postMultiply(const Matrix<T> &other) const;
238
239protected:
240 /// The current number of rows, columns, and reserved columns. The underlying
241 /// data vector is viewed as an nRows x nReservedColumns matrix, of which the
242 /// first nColumns columns are currently in use, and the remaining are
243 /// reserved columns filled with zeros.
245
246 /// Stores the data. data.size() is equal to nRows * nReservedColumns.
247 /// data.capacity() / nReservedColumns is the number of reserved rows.
249};
250
251extern template class Matrix<DynamicAPInt>;
252extern template class Matrix<Fraction>;
253
254// An inherited class for integer matrices, with no new data attributes.
255// This is only used for the matrix-related methods which apply only
256// to integers (hermite normal form computation and row normalisation).
258public:
259 IntMatrix(unsigned rows, unsigned columns, unsigned reservedRows = 0,
260 unsigned reservedColumns = 0)
261 : Matrix<DynamicAPInt>(rows, columns, reservedRows, reservedColumns) {}
262
263 IntMatrix(Matrix<DynamicAPInt> m) : Matrix<DynamicAPInt>(std::move(m)) {}
264
265 /// Return the identity matrix of the specified dimension.
266 static IntMatrix identity(unsigned dimension);
267
268 /// Given the current matrix M, returns the matrices H, U such that H is the
269 /// column hermite normal form of M, i.e. H = M * U, where U is unimodular and
270 /// the matrix H has the following restrictions:
271 /// - H is lower triangular.
272 /// - The leading coefficient (the first non-zero entry from the top, called
273 /// the pivot) of a non-zero column is always strictly below of the leading
274 /// coefficient of the column before it; moreover, it is positive.
275 /// - The elements to the right of the pivots are zero and the elements to
276 /// the left of the pivots are nonnegative and strictly smaller than the
277 /// pivot.
278 std::pair<IntMatrix, IntMatrix> computeHermiteNormalForm() const;
279
280 /// Given the current matrix M, returns the matrices U, D, V such that
281 /// UMV = D, where D is called the Smith Normal Form (SNF).
282 /// The matrices have the following properties:
283 /// - U, V are unimodular. In other words, det(U), det(V) are 1 or -1;
284 /// their inverses also contain integer entries.
285 /// - D is diagonal.
286 /// - For all i, the diagonal element D_{i, i} divides D_{i + 1, i + 1}.
287 std::tuple<IntMatrix, IntMatrix, IntMatrix> computeSmithNormalForm() const;
288
289 /// Divide the first `nCols` of the specified row by their GCD.
290 /// Returns the GCD of the first `nCols` of the specified row.
291 DynamicAPInt normalizeRow(unsigned row, unsigned nCols);
292 /// Divide the columns of the specified row by their GCD.
293 /// Returns the GCD of the columns of the specified row.
294 DynamicAPInt normalizeRow(unsigned row);
295
296 // Compute the determinant of the matrix (cubic time).
297 // Stores the integer inverse of the matrix in the pointer
298 // passed (if any). The pointer is unchanged if the inverse
299 // does not exist, which happens iff det = 0.
300 // For a matrix M, the integer inverse is the matrix M' such that
301 // M x M' = M'  M = det(M) x I.
302 // Assert-fails if the matrix is not square.
303 DynamicAPInt determinant(IntMatrix *inverse = nullptr) const;
304};
305
306// An inherited class for rational matrices, with no new data attributes.
307// This class is for functionality that only applies to matrices of fractions.
309public:
310 FracMatrix(unsigned rows, unsigned columns, unsigned reservedRows = 0,
311 unsigned reservedColumns = 0)
312 : Matrix<Fraction>(rows, columns, reservedRows, reservedColumns){};
313
315
316 explicit FracMatrix(IntMatrix m);
317
318 /// Return the identity matrix of the specified dimension.
319 static FracMatrix identity(unsigned dimension);
320
321 // Compute the determinant of the matrix (cubic time).
322 // Stores the inverse of the matrix in the pointer
323 // passed (if any). The pointer is unchanged if the inverse
324 // does not exist, which happens iff det = 0.
325 // Assert-fails if the matrix is not square.
326 Fraction determinant(FracMatrix *inverse = nullptr) const;
327
328 // Computes the Gram-Schmidt orthogonalisation
329 // of the rows of matrix (cubic time).
330 // The rows of the matrix must be linearly independent.
331 FracMatrix gramSchmidt() const;
332
333 // Run LLL basis reduction on the matrix, modifying it in-place.
334 // The parameter is what [the original
335 // paper](https://www.cs.cmu.edu/~avrim/451f11/lectures/lect1129_LLL.pdf)
336 // calls `y`, usually 3/4.
337 void LLL(const Fraction &delta);
338
339 // Multiply each row of the matrix by the LCM of the denominators, thereby
340 // converting it to an integer matrix.
341 IntMatrix normalizeRows() const;
342};
343
344} // namespace presburger
345} // namespace mlir
346
347#endif // MLIR_ANALYSIS_PRESBURGER_MATRIX_H
FracMatrix(unsigned rows, unsigned columns, unsigned reservedRows=0, unsigned reservedColumns=0)
Definition Matrix.h:310
FracMatrix(Matrix< Fraction > m)
Definition Matrix.h:314
IntMatrix(Matrix< DynamicAPInt > m)
Definition Matrix.h:263
IntMatrix(unsigned rows, unsigned columns, unsigned reservedRows=0, unsigned reservedColumns=0)
Definition Matrix.h:259
This is a class to represent a resizable matrix.
Definition Matrix.h:42
void moveColumns(unsigned srcPos, unsigned num, unsigned dstPos)
Move the columns in the source range [srcPos, srcPos + num) to the specified destination [dstPos,...
Definition Matrix.cpp:260
bool hasConsistentState() const
Return whether the Matrix is in a consistent state with all its invariants satisfied.
Definition Matrix.cpp:442
void insertRows(unsigned pos, unsigned count)
Insert rows having positions pos, pos + 1, ... pos + count - 1.
Definition Matrix.cpp:216
unsigned getNumRows() const
Definition Matrix.h:86
void swapColumns(unsigned column, unsigned otherColumn)
Swap the given columns.
Definition Matrix.cpp:120
unsigned nRows
The current number of rows, columns, and reserved columns.
Definition Matrix.h:244
void removeColumn(unsigned pos)
Definition Matrix.cpp:194
unsigned appendExtraRow()
Add an extra row at the bottom of the matrix and return its position.
Definition Matrix.cpp:65
unsigned nReservedColumns
Definition Matrix.h:244
void addToColumn(unsigned sourceColumn, unsigned targetColumn, const T &scale)
Add scale multiples of the source column to the target column.
Definition Matrix.cpp:326
Matrix< T > getSubMatrix(unsigned fromRow, unsigned toRow, unsigned fromColumn, unsigned toColumn) const
Definition Matrix.cpp:391
void print(raw_ostream &os) const
Print the matrix.
Definition Matrix.cpp:407
void copyRow(unsigned sourceRow, unsigned targetRow)
Definition Matrix.cpp:244
void scaleRow(unsigned row, const T &scale)
Multiply the specified row by a factor of scale.
Definition Matrix.cpp:320
void insertColumn(unsigned pos)
Definition Matrix.cpp:148
T at(unsigned row, unsigned column) const
Definition Matrix.h:68
MutableArrayRef< T > getRow(unsigned row)
Get a [Mutable]ArrayRef corresponding to the specified row.
Definition Matrix.cpp:130
T & operator()(unsigned row, unsigned column)
Definition Matrix.h:74
void removeColumns(unsigned pos, unsigned count)
Remove the columns having positions pos, pos + 1, ... pos + count - 1.
Definition Matrix.cpp:198
void addToColumn(unsigned sourceColumn, unsigned targetColumn, int64_t scale)
Definition Matrix.h:155
void insertColumns(unsigned pos, unsigned count)
Insert columns having positions pos, pos + 1, ... pos + count - 1.
Definition Matrix.cpp:152
void setRow(unsigned row, ArrayRef< T > elems)
Set the specified row to elems.
Definition Matrix.cpp:140
std::pair< Matrix< T >, Matrix< T > > splitByBitset(ArrayRef< int > indicator)
Split the rows of a matrix into two matrices according to which bits are 1 and which are 0 in a given...
Definition Matrix.cpp:425
void fillRow(unsigned row, int64_t value)
Definition Matrix.h:139
void removeRow(unsigned pos)
Definition Matrix.cpp:230
bool operator==(const Matrix< T > &m) const
We cannot use the default implementation of operator== as it compares fields like reservedColumns etc...
Definition Matrix.cpp:33
SmallVector< T, 16 > data
Stores the data.
Definition Matrix.h:248
unsigned getNumColumns() const
Definition Matrix.h:88
T & at(unsigned row, unsigned column)
Access the element at the specified row and column.
Definition Matrix.h:62
void resizeVertically(unsigned newNRows)
Definition Matrix.cpp:104
unsigned getNumReservedRows() const
Return the maximum number of rows/columns that can be added without incurring a reallocation.
Definition Matrix.cpp:55
unsigned getNumReservedColumns() const
Definition Matrix.h:93
Matrix< T > transpose() const
Definition Matrix.cpp:80
SmallVector< T, 8 > preMultiplyWithRow(ArrayRef< T > rowVec) const
The given vector is interpreted as a row vector v.
Definition Matrix.cpp:353
static Matrix identity(unsigned dimension)
Return the identity matrix of the specified dimension.
Definition Matrix.cpp:47
void insertRow(unsigned pos)
Definition Matrix.cpp:212
SmallVector< T, 8 > postMultiplyWithColumn(ArrayRef< T > colVec) const
The given vector is interpreted as a column vector v.
Definition Matrix.cpp:364
void negateMatrix()
Negate the entire matrix.
Definition Matrix.cpp:347
void swapRows(unsigned row, unsigned otherRow)
Swap the given rows.
Definition Matrix.cpp:110
void resizeHorizontally(unsigned newNColumns)
Definition Matrix.cpp:90
void reserveRows(unsigned rows)
Reserve enough space to resize to the specified number of rows without reallocations.
Definition Matrix.cpp:60
void negateColumn(unsigned column)
Negate the specified column.
Definition Matrix.cpp:335
void resize(unsigned newNRows, unsigned newNColumns)
Resize the matrix to the specified dimensions.
Definition Matrix.cpp:98
void fillRow(unsigned row, const T &value)
Definition Matrix.cpp:252
void addToRow(unsigned sourceRow, unsigned targetRow, const T &scale)
Add scale multiples of the source row to the target row.
Definition Matrix.cpp:306
T operator()(unsigned row, unsigned column) const
Definition Matrix.h:76
void negateRow(unsigned row)
Negate the specified row.
Definition Matrix.cpp:341
Matrix< T > postMultiply(const Matrix< T > &other) const
Returns the matrix right-multiplied with other.
Definition Matrix.cpp:288
void addToRow(unsigned sourceRow, unsigned targetRow, int64_t scale)
Definition Matrix.h:143
void removeRows(unsigned pos, unsigned count)
Remove the rows having positions pos, pos + 1, ... pos + count - 1.
Definition Matrix.cpp:234
Include the generated interface declarations.
A class to represent fractions.
Definition Fraction.h:29