MLIR 24.0.0git
Matrix.cpp
Go to the documentation of this file.
1//===- Matrix.cpp - MLIR Matrix 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
12#include "llvm/Support/MathExtras.h"
13#include "llvm/Support/raw_ostream.h"
14#include <algorithm>
15#include <cassert>
16#include <utility>
17
18using namespace mlir;
19using namespace presburger;
20
21template <typename T>
22Matrix<T>::Matrix(unsigned rows, unsigned columns, unsigned reservedRows,
23 unsigned reservedColumns)
24 : nRows(rows), nColumns(columns),
25 nReservedColumns(std::max(nColumns, reservedColumns)),
27 data.reserve(std::max(nRows, reservedRows) * nReservedColumns);
28}
29
30/// We cannot use the default implementation of operator== as it compares
31/// fields like `reservedColumns` etc., which are not part of the data.
32template <typename T>
33bool Matrix<T>::operator==(const Matrix<T> &m) const {
34 if (nRows != m.getNumRows())
35 return false;
36 if (nColumns != m.getNumColumns())
37 return false;
38
39 for (unsigned i = 0; i < nRows; i++)
40 if (getRow(i) != m.getRow(i))
41 return false;
42
43 return true;
44}
45
46template <typename T>
47Matrix<T> Matrix<T>::identity(unsigned dimension) {
48 Matrix matrix(dimension, dimension);
49 for (unsigned i = 0; i < dimension; ++i)
50 matrix(i, i) = 1;
51 return matrix;
52}
53
54template <typename T>
56 return data.capacity() / nReservedColumns;
57}
58
59template <typename T>
60void Matrix<T>::reserveRows(unsigned rows) {
61 data.reserve(rows * nReservedColumns);
62}
63
64template <typename T>
67 return nRows - 1;
68}
69
70template <typename T>
72 assert(elems.size() == nColumns && "elems must match row length!");
73 unsigned row = appendExtraRow();
74 for (unsigned col = 0; col < nColumns; ++col)
75 at(row, col) = elems[col];
76 return row;
77}
79template <typename T>
82 for (unsigned row = 0; row < nRows; ++row)
83 for (unsigned col = 0; col < nColumns; ++col)
84 transp(col, row) = at(row, col);
85
86 return transp;
87}
88
89template <typename T>
90void Matrix<T>::resizeHorizontally(unsigned newNColumns) {
91 if (newNColumns < nColumns)
92 removeColumns(newNColumns, nColumns - newNColumns);
93 if (newNColumns > nColumns)
94 insertColumns(nColumns, newNColumns - nColumns);
95}
96
97template <typename T>
98void Matrix<T>::resize(unsigned newNRows, unsigned newNColumns) {
99 resizeHorizontally(newNColumns);
101}
102
103template <typename T>
104void Matrix<T>::resizeVertically(unsigned newNRows) {
105 nRows = newNRows;
106 data.resize(nRows * nReservedColumns);
107}
108
109template <typename T>
110void Matrix<T>::swapRows(unsigned row, unsigned otherRow) {
111 assert((row < getNumRows() && otherRow < getNumRows()) &&
112 "Given row out of bounds");
113 if (row == otherRow)
114 return;
115 for (unsigned col = 0; col < nColumns; col++)
116 std::swap(at(row, col), at(otherRow, col));
119template <typename T>
120void Matrix<T>::swapColumns(unsigned column, unsigned otherColumn) {
121 assert((column < getNumColumns() && otherColumn < getNumColumns()) &&
122 "Given column out of bounds");
123 if (column == otherColumn)
124 return;
125 for (unsigned row = 0; row < nRows; row++)
126 std::swap(at(row, column), at(row, otherColumn));
127}
128
129template <typename T>
131 return {&data[row * nReservedColumns], nColumns};
132}
134template <typename T>
135ArrayRef<T> Matrix<T>::getRow(unsigned row) const {
136 return {&data[row * nReservedColumns], nColumns};
137}
139template <typename T>
140void Matrix<T>::setRow(unsigned row, ArrayRef<T> elems) {
141 assert(elems.size() == getNumColumns() &&
142 "elems size must match row length!");
143 for (unsigned i = 0, e = getNumColumns(); i < e; ++i)
144 at(row, i) = elems[i];
145}
146
147template <typename T>
148void Matrix<T>::insertColumn(unsigned pos) {
149 insertColumns(pos, 1);
151template <typename T>
152void Matrix<T>::insertColumns(unsigned pos, unsigned count) {
153 if (count == 0)
154 return;
155 assert(pos <= nColumns);
156 unsigned oldNReservedColumns = nReservedColumns;
157 if (nColumns + count > nReservedColumns) {
158 nReservedColumns = llvm::NextPowerOf2(nColumns + count);
159 data.resize(nRows * nReservedColumns);
160 }
161 nColumns += count;
162
163 int colStart = nColumns - 1;
164 if (oldNReservedColumns != nReservedColumns)
165 colStart = nReservedColumns - 1;
166 for (int ri = nRows - 1; ri >= 0; --ri) {
167 for (int ci = colStart; ci >= 0; --ci) {
168 unsigned r = ri;
169 unsigned c = ci;
170 T &dest = data[r * nReservedColumns + c];
171 if (c >= nColumns) { // NOLINT
172 // Out of bounds columns are zero-initialized. NOLINT because clang-tidy
173 // complains about this branch being the same as the c >= pos one.
174 dest = 0;
175 } else if (c >= pos + count) {
176 // Shift the data occuring after the inserted columns.
177 dest = data[r * oldNReservedColumns + c - count];
178 } else if (c >= pos) {
179 // The inserted columns are also zero-initialized.
180 dest = 0;
181 } else {
182 // The columns before the inserted columns stay at the same (row, col)
183 // but this corresponds to a different location in the linearized array
184 // if the number of reserved columns changed.
185 if (nReservedColumns == oldNReservedColumns)
186 break;
187 dest = data[r * oldNReservedColumns + c];
188 }
190 }
191}
192
193template <typename T>
194void Matrix<T>::removeColumn(unsigned pos) {
196}
197template <typename T>
198void Matrix<T>::removeColumns(unsigned pos, unsigned count) {
199 if (count == 0)
200 return;
201 assert(pos + count - 1 < nColumns);
202 for (unsigned r = 0; r < nRows; ++r) {
203 for (unsigned c = pos; c < nColumns - count; ++c)
204 at(r, c) = at(r, c + count);
205 for (unsigned c = nColumns - count; c < nColumns; ++c)
206 at(r, c) = 0;
207 }
208 nColumns -= count;
210
211template <typename T>
212void Matrix<T>::insertRow(unsigned pos) {
213 insertRows(pos, 1);
215template <typename T>
216void Matrix<T>::insertRows(unsigned pos, unsigned count) {
217 if (count == 0)
218 return;
220 assert(pos <= nRows);
221 resizeVertically(nRows + count);
222 for (int r = nRows - 1; r >= int(pos + count); --r)
223 copyRow(r - count, r);
224 for (int r = pos + count - 1; r >= int(pos); --r)
225 for (unsigned c = 0; c < nColumns; ++c)
226 at(r, c) = 0;
227}
228
229template <typename T>
230void Matrix<T>::removeRow(unsigned pos) {
231 removeRows(pos, 1);
232}
233template <typename T>
234void Matrix<T>::removeRows(unsigned pos, unsigned count) {
235 if (count == 0)
236 return;
237 assert(pos + count - 1 <= nRows);
238 for (unsigned r = pos; r + count < nRows; ++r)
239 copyRow(r + count, r);
241}
242
243template <typename T>
244void Matrix<T>::copyRow(unsigned sourceRow, unsigned targetRow) {
245 if (sourceRow == targetRow)
246 return;
247 for (unsigned c = 0; c < nColumns; ++c)
248 at(targetRow, c) = at(sourceRow, c);
249}
250
251template <typename T>
252void Matrix<T>::fillRow(unsigned row, const T &value) {
253 for (unsigned col = 0; col < nColumns; ++col)
254 at(row, col) = value;
255}
256
257// moveColumns is implemented by moving the columns adjacent to the source range
258// to their final position.
259template <typename T>
260void Matrix<T>::moveColumns(unsigned srcPos, unsigned num, unsigned dstPos) {
261 if (num == 0)
262 return;
263
264 if (dstPos == srcPos)
265 return;
266
267 assert(srcPos + num <= getNumColumns() &&
268 "move source range exceeds matrix columns");
269 assert(dstPos + num <= getNumColumns() &&
270 "move destination range exceeds matrix columns");
271
272 unsigned numRows = getNumRows();
273 // std::rotate(start, middle, end) permutes the elements of [start, end] to
274 // [middle, end) + [start, middle). NOTE: &at(i, srcPos + num) will trigger an
275 // assert.
276 if (dstPos > srcPos) {
277 for (unsigned i = 0; i < numRows; ++i) {
278 std::rotate(&at(i, srcPos), &at(i, srcPos) + num, &at(i, dstPos) + num);
279 }
280 return;
281 }
282 for (unsigned i = 0; i < numRows; ++i) {
283 std::rotate(&at(i, dstPos), &at(i, srcPos), &at(i, srcPos) + num);
284 }
285}
286
287template <typename T>
289 assert(getNumColumns() == other.getNumRows());
290 unsigned n = getNumRows();
291 unsigned m = other.getNumRows();
292 unsigned p = other.getNumColumns();
293 Matrix<T> result(n, p);
294
295 for (unsigned i = 0; i < n; i++) {
296 for (unsigned j = 0; j < m; j++) {
297 for (unsigned k = 0; k < p; k++) {
298 result.at(i, k) += at(i, j) * other.at(j, k);
299 }
300 }
301 }
302 return result;
303}
304
305template <typename T>
306void Matrix<T>::addToRow(unsigned sourceRow, unsigned targetRow,
307 const T &scale) {
308 addToRow(targetRow, getRow(sourceRow), scale);
309}
310
311template <typename T>
312void Matrix<T>::addToRow(unsigned row, ArrayRef<T> rowVec, const T &scale) {
313 if (scale == 0)
314 return;
315 for (unsigned col = 0; col < nColumns; ++col)
316 at(row, col) += scale * rowVec[col];
317}
318
319template <typename T>
320void Matrix<T>::scaleRow(unsigned row, const T &scale) {
321 for (unsigned col = 0; col < nColumns; ++col)
322 at(row, col) *= scale;
323}
324
325template <typename T>
326void Matrix<T>::addToColumn(unsigned sourceColumn, unsigned targetColumn,
327 const T &scale) {
328 if (scale == 0)
329 return;
330 for (unsigned row = 0, e = getNumRows(); row < e; ++row)
331 at(row, targetColumn) += scale * at(row, sourceColumn);
332}
333
334template <typename T>
335void Matrix<T>::negateColumn(unsigned column) {
336 for (unsigned row = 0, e = getNumRows(); row < e; ++row)
337 at(row, column) = -at(row, column);
338}
339
340template <typename T>
341void Matrix<T>::negateRow(unsigned row) {
342 for (unsigned column = 0, e = getNumColumns(); column < e; ++column)
343 at(row, column) = -at(row, column);
344}
345
346template <typename T>
348 for (unsigned row = 0; row < nRows; ++row)
349 negateRow(row);
350}
351
352template <typename T>
354 assert(rowVec.size() == getNumRows() && "Invalid row vector dimension!");
355
357 for (unsigned col = 0, e = getNumColumns(); col < e; ++col)
358 for (unsigned i = 0, e = getNumRows(); i < e; ++i)
359 result[col] += rowVec[i] * at(i, col);
360 return result;
361}
362
363template <typename T>
365 assert(getNumColumns() == colVec.size() &&
366 "Invalid column vector dimension!");
367
369 for (unsigned row = 0, e = getNumRows(); row < e; row++)
370 for (unsigned i = 0, e = getNumColumns(); i < e; i++)
371 result[row] += at(row, i) * colVec[i];
372 return result;
373}
374
375/// Set M(row, targetCol) to its remainder on division by M(row, sourceCol)
376/// by subtracting from column targetCol an appropriate integer multiple of
377/// sourceCol. This brings M(row, targetCol) to the range [0, M(row,
378/// sourceCol)). Apply the same column operation to otherMatrix, with the same
379/// integer multiple.
381 unsigned sourceCol, unsigned targetCol,
382 Matrix<DynamicAPInt> &otherMatrix) {
383 assert(m(row, sourceCol) != 0 && "Cannot divide by zero!");
384 assert(m(row, sourceCol) > 0 && "Source must be positive!");
385 DynamicAPInt ratio = -floorDiv(m(row, targetCol), m(row, sourceCol));
386 m.addToColumn(sourceCol, targetCol, ratio);
387 otherMatrix.addToColumn(sourceCol, targetCol, ratio);
388}
389
390template <typename T>
391Matrix<T> Matrix<T>::getSubMatrix(unsigned fromRow, unsigned toRow,
392 unsigned fromColumn,
393 unsigned toColumn) const {
394 assert(fromRow <= toRow && "end of row range must be after beginning!");
395 assert(toRow <= nRows && "end of row range out of bounds!");
396 assert(fromColumn <= toColumn &&
397 "end of column range must be after beginning!");
398 assert(toColumn <= nColumns && "end of column range out of bounds!");
399 Matrix<T> subMatrix(toRow - fromRow, toColumn - fromColumn);
400 for (unsigned i = fromRow; i < toRow; ++i)
401 for (unsigned j = fromColumn; j < toColumn; ++j)
402 subMatrix(i - fromRow, j - fromColumn) = at(i, j);
403 return subMatrix;
404}
405
406template <typename T>
408 PrintTableMetrics ptm = {0, 0, "-"};
409 for (unsigned row = 0; row < nRows; ++row)
410 for (unsigned column = 0; column < nColumns; ++column)
411 updatePrintMetrics<T>(at(row, column), ptm);
412 unsigned minSpacing = 1;
413 for (unsigned row = 0; row < nRows; ++row) {
414 for (unsigned column = 0; column < nColumns; ++column) {
415 printWithPrintMetrics<T>(os, at(row, column), minSpacing, ptm);
416 }
417 os << "\n";
418 }
419}
420
421/// We iterate over the `indicator` bitset, checking each bit. If a bit is 1,
422/// we append it to one matrix, and if it is zero, we append it to the other.
423template <typename T>
424std::pair<Matrix<T>, Matrix<T>>
426 Matrix<T> rowsForOne(0, nColumns), rowsForZero(0, nColumns);
427 for (unsigned i = 0; i < nRows; i++) {
428 if (indicator[i] == 1)
429 rowsForOne.appendExtraRow(getRow(i));
430 else
431 rowsForZero.appendExtraRow(getRow(i));
432 }
433 return {rowsForOne, rowsForZero};
434}
435
436#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
437template <typename T>
438void Matrix<T>::dump() const {
439 print(llvm::errs());
440}
441#endif
442
443template <typename T>
445 if (data.size() != nRows * nReservedColumns)
446 return false;
448 return false;
449#ifdef EXPENSIVE_CHECKS
450 for (unsigned r = 0; r < nRows; ++r)
451 for (unsigned c = nColumns; c < nReservedColumns; ++c)
452 if (data[r * nReservedColumns + c] != 0)
453 return false;
454#endif
455 return true;
456}
457
458namespace mlir {
459namespace presburger {
460template class Matrix<DynamicAPInt>;
461template class Matrix<Fraction>;
462} // namespace presburger
463} // namespace mlir
464
465IntMatrix IntMatrix::identity(unsigned dimension) {
466 IntMatrix matrix(dimension, dimension);
467 for (unsigned i = 0; i < dimension; ++i)
468 matrix(i, i) = 1;
469 return matrix;
470}
471
474 for (unsigned i = 0; i < nRows; i++)
475 for (unsigned j = 0; j < nColumns; j++)
476 mat(i, j) = at(i, j);
477
478 return mat;
479}
480
481std::pair<IntMatrix, IntMatrix> IntMatrix::computeHermiteNormalForm() const {
482 // We start with u as an identity matrix and perform operations on h until h
483 // is in hermite normal form. We apply the same sequence of operations on u to
484 // obtain a transform that takes h to hermite normal form.
485 IntMatrix h = *this;
487
488 unsigned echelonCol = 0;
489 // Invariant: in all rows above row, all columns from echelonCol onwards
490 // are all zero elements. In an iteration, if the curent row has any non-zero
491 // elements echelonCol onwards, we bring one to echelonCol and use it to
492 // make all elements echelonCol + 1 onwards zero.
493 for (unsigned row = 0; row < h.getNumRows(); ++row) {
494 // Search row for a non-empty entry, starting at echelonCol.
495 unsigned nonZeroCol = echelonCol;
496 for (unsigned e = h.getNumColumns(); nonZeroCol < e; ++nonZeroCol) {
497 if (h(row, nonZeroCol) == 0)
498 continue;
499 break;
500 }
501
502 // Continue to the next row with the same echelonCol if this row is all
503 // zeros from echelonCol onwards.
504 if (nonZeroCol == h.getNumColumns())
505 continue;
506
507 // Bring the non-zero column to echelonCol. This doesn't affect rows
508 // above since they are all zero at these columns.
509 if (nonZeroCol != echelonCol) {
510 h.swapColumns(nonZeroCol, echelonCol);
511 u.swapColumns(nonZeroCol, echelonCol);
512 }
513
514 // Make h(row, echelonCol) non-negative.
515 if (h(row, echelonCol) < 0) {
516 h.negateColumn(echelonCol);
517 u.negateColumn(echelonCol);
518 }
519
520 // Make all the entries in row after echelonCol zero.
521 for (unsigned i = echelonCol + 1, e = h.getNumColumns(); i < e; ++i) {
522 // We make h(row, i) non-negative, and then apply the Euclidean GCD
523 // algorithm to (row, i) and (row, echelonCol). At the end, one of them
524 // has value equal to the gcd of the two entries, and the other is zero.
525
526 if (h(row, i) < 0) {
527 h.negateColumn(i);
528 u.negateColumn(i);
529 }
530
531 unsigned targetCol = i, sourceCol = echelonCol;
532 // At every step, we set h(row, targetCol) %= h(row, sourceCol), and
533 // swap the indices sourceCol and targetCol. (not the columns themselves)
534 // This modulo is implemented as a subtraction
535 // h(row, targetCol) -= quotient * h(row, sourceCol),
536 // where quotient = floor(h(row, targetCol) / h(row, sourceCol)),
537 // which brings h(row, targetCol) to the range [0, h(row, sourceCol)).
538 //
539 // We are only allowed column operations; we perform the above
540 // for every row, i.e., the above subtraction is done as a column
541 // operation. This does not affect any rows above us since they are
542 // guaranteed to be zero at these columns.
543 while (h(row, targetCol) != 0 && h(row, sourceCol) != 0) {
544 modEntryColumnOperation(h, row, sourceCol, targetCol, u);
545 std::swap(targetCol, sourceCol);
546 }
547
548 // One of (row, echelonCol) and (row, i) is zero and the other is the gcd.
549 // Make it so that (row, echelonCol) holds the non-zero value.
550 if (h(row, echelonCol) == 0) {
551 h.swapColumns(i, echelonCol);
552 u.swapColumns(i, echelonCol);
553 }
554 }
555
556 // Make all entries before echelonCol non-negative and strictly smaller
557 // than the pivot entry.
558 for (unsigned i = 0; i < echelonCol; ++i)
559 modEntryColumnOperation(h, row, echelonCol, i, u);
560
561 ++echelonCol;
562 }
563
564 return {h, u};
565}
566
567// In the submatrix `mat(from:, from:)`, the function finds the position (row,
568// col) of the element with smallest non-zero absolute value. When all elements
569// in the submatrix are zero, returns std::nullopt.
570static std::optional<std::pair<unsigned, unsigned>>
571findNonZeroMinInSubmatrix(const IntMatrix &mat, unsigned from) {
572 unsigned numRows = mat.getNumRows();
573 unsigned numCols = mat.getNumColumns();
574 unsigned minRow = from, minCol = from;
575
576 std::optional<DynamicAPInt> minVal;
577 for (unsigned r = from; r < numRows; r++) {
578 for (unsigned c = from; c < numCols; c++) {
579 DynamicAPInt val = llvm::abs(mat(r, c));
580 if (val == 0 || (minVal && val >= *minVal))
581 continue;
582
583 minVal = val;
584 minRow = r;
585 minCol = c;
586 }
587 }
588
589 if (!minVal)
590 return std::nullopt;
591
592 return std::make_pair(minRow, minCol);
593}
594
595// Finds the first row in submatrix `mat(from:, from:)` that contains an element
596// `d` such that `d` is not a multiple of `divisor`. When there is no such row,
597// returns std::nullopt.
598static std::optional<unsigned> findNonMultipleRow(const IntMatrix &mat,
599 unsigned from,
600 const DynamicAPInt &divisor) {
601 unsigned numRows = mat.getNumRows();
602 unsigned numCols = mat.getNumColumns();
603 for (unsigned row = from; row < numRows; ++row) {
604 for (unsigned col = from; col < numCols; ++col) {
605 if (mat(row, col) % divisor != 0)
606 return row;
607 }
608 }
609 return std::nullopt;
610}
611
612std::tuple<IntMatrix, IntMatrix, IntMatrix>
614 IntMatrix d = *this;
615 // We put D into diagonal form by applying row and columns operations to it.
616 // The matrix U records row operations applied in the process, and V records
617 // column operations.
620
621 unsigned numRows = d.getNumRows();
622 unsigned numCols = d.getNumColumns();
623 for (unsigned i = 0, e = std::min(numRows, numCols); i < e; i++) {
624 // We first put D into diagonal form, and then ensure the divisibility
625 // condition. The latter step is better illustrated with an example:
626 //
627 // [6 0 ] ---(1)--> [6 10] ---(2)--> [2 0 ]
628 // [0 10] [0 10] [0 10]
629 //
630 // (1) adds the element violating the divisibility constraint to the same
631 // column in row i;
632 // (2) does an elimination of the column.
633 //
634 // There can be many elements that violate the constraint, hence the loop.
635 bool changed;
636 do {
637 changed = false;
638
639 // Find the entry in the submatrix d(i:, i:) with the smallest non-zero
640 // absolute value.
641 // The element is the pivot, and we record its current row and column.
642 auto pivotPos = findNonZeroMinInSubmatrix(d, i);
643 if (!pivotPos)
644 break;
645 auto [pvtRow, pvtCol] = *pivotPos;
646
647 // The remaining submatrix is zero.
648 if (d(pvtRow, pvtCol) == 0)
649 break;
650
651 // Bring pivot to d(i, i). Record the operation in u, v respectively.
652 if (pvtRow != i) {
653 d.swapRows(pvtRow, i);
654 u.swapRows(pvtRow, i);
655 }
656 if (pvtCol != i) {
657 d.swapColumns(pvtCol, i);
658 v.swapColumns(pvtCol, i);
659 }
660
661 // Ensure the pivot is positive.
662 if (d(i, i) < 0) {
663 d.negateRow(i);
664 u.negateRow(i);
665 }
666
667 // Clear other entries in row i and column i with Euclid's algorithm.
668 for (unsigned r = i + 1; r < numRows; ++r) {
669 while (d(r, i) != 0) {
670 DynamicAPInt quotient = d(r, i) / d(i, i);
671 d.addToRow(i, r, -quotient);
672 u.addToRow(i, r, -quotient);
673
674 if (d(r, i) != 0) {
675 d.swapRows(r, i);
676 u.swapRows(r, i);
677 changed = true;
678 }
679 }
680 }
681 // Similar to the rows operations, this time it works on columns.
682 for (unsigned c = i + 1; c < numCols; ++c) {
683 while (d(i, c) != 0) {
684 DynamicAPInt quotient = d(i, c) / d(i, i);
685 d.addToColumn(i, c, -quotient);
686 v.addToColumn(i, c, -quotient);
687
688 if (d(i, c) != 0) {
689 d.swapColumns(c, i);
690 v.swapColumns(c, i);
691 changed = true;
692 }
693 }
694 }
695
696 if (auto row = findNonMultipleRow(d, i + 1, d(i, i))) {
697 // Add the row (r) to row i. This brings d(r, c) into the i-th row,
698 // creating a new value at d(i, c) that will be used to reduce the
699 // pivot size.
700 d.addToRow(*row, i, 1);
701 u.addToRow(*row, i, 1);
702 changed = true;
703 }
704 } while (changed);
705 }
706
707 return {u, d, v};
708}
709
710DynamicAPInt IntMatrix::normalizeRow(unsigned row, unsigned cols) {
711 return normalizeRange(getRow(row).slice(0, cols));
712}
713
714DynamicAPInt IntMatrix::normalizeRow(unsigned row) {
715 return normalizeRow(row, getNumColumns());
716}
717
718DynamicAPInt IntMatrix::determinant(IntMatrix *inverse) const {
719 assert(nRows == nColumns &&
720 "determinant can only be calculated for square matrices!");
721
722 FracMatrix m(*this);
723
724 FracMatrix fracInverse(nRows, nColumns);
725 DynamicAPInt detM = m.determinant(&fracInverse).getAsInteger();
726
727 if (detM == 0)
728 return DynamicAPInt(0);
729
730 if (!inverse)
731 return detM;
732
733 *inverse = IntMatrix(nRows, nColumns);
734 for (unsigned i = 0; i < nRows; i++)
735 for (unsigned j = 0; j < nColumns; j++)
736 inverse->at(i, j) = (fracInverse.at(i, j) * detM).getAsInteger();
737
738 return detM;
739}
740
741FracMatrix FracMatrix::identity(unsigned dimension) {
742 return Matrix::identity(dimension);
743}
744
747 for (unsigned i = 0; i < nRows; i++)
748 for (unsigned j = 0; j < nColumns; j++)
749 mat(i, j) = at(i, j).getAsInteger();
750
751 return mat;
752}
753
756 for (unsigned i = 0, r = m.getNumRows(); i < r; i++)
757 for (unsigned j = 0, c = m.getNumColumns(); j < c; j++)
758 this->at(i, j) = m.at(i, j);
759}
760
762 assert(nRows == nColumns &&
763 "determinant can only be calculated for square matrices!");
764
765 FracMatrix m(*this);
766 FracMatrix tempInv(nRows, nColumns);
767 if (inverse)
768 tempInv = FracMatrix::identity(nRows);
769
770 Fraction a, b;
771 // Make the matrix into upper triangular form using
772 // gaussian elimination with row operations.
773 // If inverse is required, we apply more operations
774 // to turn the matrix into diagonal form. We apply
775 // the same operations to the inverse matrix,
776 // which is initially identity.
777 // Either way, the product of the diagonal elements
778 // is then the determinant.
779 for (unsigned i = 0; i < nRows; i++) {
780 if (m(i, i) == 0)
781 // First ensure that the diagonal
782 // element is nonzero, by swapping
783 // it with a nonzero row.
784 for (unsigned j = i + 1; j < nRows; j++) {
785 if (m(j, i) != 0) {
786 m.swapRows(j, i);
787 if (inverse)
788 tempInv.swapRows(j, i);
789 break;
790 }
791 }
792
793 b = m.at(i, i);
794 if (b == 0)
795 return 0;
796
797 // Set all elements above the
798 // diagonal to zero.
799 if (inverse) {
800 for (unsigned j = 0; j < i; j++) {
801 if (m.at(j, i) == 0)
802 continue;
803 a = m.at(j, i);
804 // Set element (j, i) to zero
805 // by subtracting the ith row,
806 // appropriately scaled.
807 m.addToRow(i, j, -a / b);
808 tempInv.addToRow(i, j, -a / b);
809 }
810 }
811
812 // Set all elements below the
813 // diagonal to zero.
814 for (unsigned j = i + 1; j < nRows; j++) {
815 if (m.at(j, i) == 0)
816 continue;
817 a = m.at(j, i);
818 // Set element (j, i) to zero
819 // by subtracting the ith row,
820 // appropriately scaled.
821 m.addToRow(i, j, -a / b);
822 if (inverse)
823 tempInv.addToRow(i, j, -a / b);
824 }
825 }
826
827 // Now only diagonal elements of m are nonzero, but they are
828 // not necessarily 1. To get the true inverse, we should
829 // normalize them and apply the same scale to the inverse matrix.
830 // For efficiency we skip scaling m and just scale tempInv appropriately.
831 if (inverse) {
832 for (unsigned i = 0; i < nRows; i++)
833 for (unsigned j = 0; j < nRows; j++)
834 tempInv.at(i, j) = tempInv.at(i, j) / m(i, i);
835
836 *inverse = std::move(tempInv);
837 }
838
840 for (unsigned i = 0; i < nRows; i++)
841 determinant *= m.at(i, i);
842
843 return determinant;
844}
845
847 // Create a copy of the argument to store
848 // the orthogonalised version.
849 FracMatrix orth(*this);
850
851 // For each vector (row) in the matrix, subtract its unit
852 // projection along each of the previous vectors.
853 // This ensures that it has no component in the direction
854 // of any of the previous vectors.
855 for (unsigned i = 1, e = getNumRows(); i < e; i++) {
856 for (unsigned j = 0; j < i; j++) {
857 Fraction jNormSquared = dotProduct(orth.getRow(j), orth.getRow(j));
858 assert(jNormSquared != 0 && "some row became zero! Inputs to this "
859 "function must be linearly independent.");
860 Fraction projectionScale =
861 dotProduct(orth.getRow(i), orth.getRow(j)) / jNormSquared;
862 orth.addToRow(j, i, -projectionScale);
863 }
864 }
865 return orth;
866}
867
868// Convert the matrix, interpreted (row-wise) as a basis
869// to an LLL-reduced basis.
870//
871// This is an implementation of the algorithm described in
872// "Factoring polynomials with rational coefficients" by
873// A. K. Lenstra, H. W. Lenstra Jr., L. Lovasz.
874//
875// Let {b_1, ..., b_n} be the current basis and
876// {b_1*, ..., b_n*} be the Gram-Schmidt orthogonalised
877// basis (unnormalized).
878// Define the Gram-Schmidt coefficients μ_ij as
879// (b_i • b_j*) / (b_j* • b_j*), where (•) represents the inner product.
880//
881// We iterate starting from the second row to the last row.
882//
883// For the kth row, we first check μ_kj for all rows j < k.
884// We subtract b_j (scaled by the integer nearest to μ_kj)
885// from b_k.
886//
887// Now, we update k.
888// If b_k and b_{k-1} satisfy the Lovasz condition
889// |b_k|^2 ≥ (δ - μ_k{k-1}^2) |b_{k-1}|^2,
890// we are done and we increment k.
891// Otherwise, we swap b_k and b_{k-1} and decrement k.
892//
893// We repeat this until k = n and return.
894void FracMatrix::LLL(const Fraction &delta) {
895 DynamicAPInt nearest;
896 Fraction mu;
897
898 // `gsOrth` holds the Gram-Schmidt orthogonalisation
899 // of the matrix at all times. It is recomputed every
900 // time the matrix is modified during the algorithm.
901 // This is naive and can be optimised.
902 FracMatrix gsOrth = gramSchmidt();
903
904 // We start from the second row.
905 unsigned k = 1;
906 while (k < getNumRows()) {
907 for (unsigned j = k - 1; j < k; j--) {
908 // Compute the Gram-Schmidt coefficient μ_jk.
909 mu = dotProduct(getRow(k), gsOrth.getRow(j)) /
910 dotProduct(gsOrth.getRow(j), gsOrth.getRow(j));
911 nearest = round(mu);
912 // Subtract b_j scaled by the integer nearest to μ_jk from b_k.
913 addToRow(k, getRow(j), -Fraction(nearest, 1));
914 gsOrth = gramSchmidt(); // Update orthogonalization.
915 }
916 mu = dotProduct(getRow(k), gsOrth.getRow(k - 1)) /
917 dotProduct(gsOrth.getRow(k - 1), gsOrth.getRow(k - 1));
918 // Check the Lovasz condition for b_k and b_{k-1}.
919 if (dotProduct(gsOrth.getRow(k), gsOrth.getRow(k)) >
920 (delta - mu * mu) *
921 dotProduct(gsOrth.getRow(k - 1), gsOrth.getRow(k - 1))) {
922 // If it is satisfied, proceed to the next k.
923 k += 1;
924 } else {
925 // If it is not satisfied, decrement k (without
926 // going beyond the second row).
927 swapRows(k, k - 1);
928 gsOrth = gramSchmidt(); // Update orthogonalization.
929 k = k > 1 ? k - 1 : 1;
930 }
931 }
932}
933
935 unsigned numRows = getNumRows();
936 unsigned numColumns = getNumColumns();
937 IntMatrix normalized(numRows, numColumns);
938
939 DynamicAPInt lcmDenoms = DynamicAPInt(1);
940 for (unsigned i = 0; i < numRows; i++) {
941 // For a row, first compute the LCM of the denominators.
942 for (unsigned j = 0; j < numColumns; j++)
943 lcmDenoms = lcm(lcmDenoms, at(i, j).den);
944 // Then, multiply by it throughout and convert to integers.
945 for (unsigned j = 0; j < numColumns; j++)
946 normalized(i, j) = (at(i, j) * lcmDenoms).getAsInteger();
947 }
948 return normalized;
949}
for(Operation *op :ops)
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static std::optional< std::pair< unsigned, unsigned > > findNonZeroMinInSubmatrix(const IntMatrix &mat, unsigned from)
Definition Matrix.cpp:571
static void modEntryColumnOperation(Matrix< DynamicAPInt > &m, unsigned row, unsigned sourceCol, unsigned targetCol, Matrix< DynamicAPInt > &otherMatrix)
Set M(row, targetCol) to its remainder on division by M(row, sourceCol) by subtracting from column ta...
Definition Matrix.cpp:380
static std::optional< unsigned > findNonMultipleRow(const IntMatrix &mat, unsigned from, const DynamicAPInt &divisor)
Definition Matrix.cpp:598
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static void print(spirv::VerCapExtAttr triple, DialectAsmPrinter &printer)
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
FracMatrix gramSchmidt() const
Definition Matrix.cpp:846
IntMatrix normalizeRows() const
Definition Matrix.cpp:934
static FracMatrix identity(unsigned dimension)
Return the identity matrix of the specified dimension.
Definition Matrix.cpp:741
FracMatrix(unsigned rows, unsigned columns, unsigned reservedRows=0, unsigned reservedColumns=0)
Definition Matrix.h:317
Fraction determinant(FracMatrix *inverse=nullptr) const
Definition Matrix.cpp:761
void LLL(const Fraction &delta)
Definition Matrix.cpp:894
IntMatrix asIntMatrix() const
Definition Matrix.cpp:745
static IntMatrix identity(unsigned dimension)
Return the identity matrix of the specified dimension.
Definition Matrix.cpp:465
std::pair< IntMatrix, IntMatrix > computeHermiteNormalForm() const
Given the current matrix M, returns the matrices H, U such that H is the column hermite normal form o...
Definition Matrix.cpp:481
DynamicAPInt determinant(IntMatrix *inverse=nullptr) const
Definition Matrix.cpp:718
FracMatrix asFracMatrix() const
Definition Matrix.cpp:472
IntMatrix(unsigned rows, unsigned columns, unsigned reservedRows=0, unsigned reservedColumns=0)
Definition Matrix.h:263
DynamicAPInt normalizeRow(unsigned row, unsigned nCols)
Divide the first nCols of the specified row by their GCD.
Definition Matrix.cpp:710
std::tuple< IntMatrix, IntMatrix, IntMatrix > computeSmithNormalForm() const
Given the current matrix M, returns the matrices U, D, V such that UMV = D, where D is called the Smi...
Definition Matrix.cpp:613
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:444
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:247
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:247
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
MutableArrayRef< T > getRow(unsigned row)
Get a [Mutable]ArrayRef corresponding to the specified row.
Definition Matrix.cpp:130
void removeColumns(unsigned pos, unsigned count)
Definition Matrix.cpp:198
void insertColumns(unsigned pos, unsigned count)
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 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:251
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
LLVM_DUMP_METHOD void dump() const
Definition Matrix.cpp:438
unsigned getNumReservedRows() const
Return the maximum number of rows/columns that can be added without incurring a reallocation.
Definition Matrix.cpp:55
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
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 removeRows(unsigned pos, unsigned count)
Remove the rows having positions pos, pos + 1, ... pos + count - 1.
Definition Matrix.cpp:234
DynamicAPInt round(const Fraction &f)
Definition Fraction.h:136
Fraction dotProduct(ArrayRef< Fraction > a, ArrayRef< Fraction > b)
Compute the dot product of two vectors.
Definition Utils.cpp:539
void printWithPrintMetrics(raw_ostream &os, T val, unsigned minSpacing, const PrintTableMetrics &m)
Print val in the table with metrics specified in 'm'.
Definition Utils.h:330
void updatePrintMetrics(T val, PrintTableMetrics &m)
Iterate over each val in the table and update 'm' where .maxPreIndent and .maxPostIndent are initiali...
Definition Utils.h:316
DynamicAPInt normalizeRange(MutableArrayRef< DynamicAPInt > range)
Divide the range by its gcd and return the gcd.
Definition Utils.cpp:350
Include the generated interface declarations.
A class to represent fractions.
Definition Fraction.h:29
DynamicAPInt getAsInteger() const
Definition Fraction.h:51
Example usage: Print .12, 3.4, 56.7 preAlign = ".", minSpacing = 1, .12 .12 3.4 3....
Definition Utils.h:305
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.