Skip to content

[MLIR][Presburger] Implement matrix inverse #67382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 99 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
8b09dcb
Update upstream branch
Abhinav271828 Sep 6, 2023
5733a7e
Shift changes to Fraction
Abhinav271828 Sep 5, 2023
9b2a171
Update documentation and remove extraneous include
Abhinav271828 Sep 5, 2023
732aa35
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
7ef46f4
Fix rebase conflict
Abhinav271828 Sep 1, 2023
89ad6f9
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
b9586b1
Add static assert
Abhinav271828 Sep 4, 2023
dfd4e7c
Fix comment
Abhinav271828 Sep 5, 2023
ea5256e
Make compatible with fraction patch
Abhinav271828 Sep 5, 2023
a342259
Revert "Make compatible with fraction patch"
Abhinav271828 Sep 5, 2023
24bee0e
Remove duplicate definitions
Abhinav271828 Sep 5, 2023
ac6d4a6
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
6d2d304
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
2c2909f
Add static assert
Abhinav271828 Sep 4, 2023
c94405f
Fix duplicates and use increment operators for Fraction
Abhinav271828 Sep 6, 2023
e65109d
Fix duplicates and delete extra files
Abhinav271828 Sep 6, 2023
c668acd
Fix comment
Abhinav271828 Sep 18, 2023
8e863db
Create IntMatrix for hermite normal form and row normalisation
Abhinav271828 Sep 20, 2023
63c81aa
Put opening braces on same line
Abhinav271828 Sep 20, 2023
2e59d89
Merge branch 'llvm:main' into first_patch
Abhinav271828 Sep 25, 2023
80f1d96
Inherit Matrix<Fraction> to FracMatrix and define inverse
Abhinav271828 Sep 25, 2023
69cce30
Add test for matrix inverse
Abhinav271828 Sep 25, 2023
033a110
Formatting
Abhinav271828 Sep 26, 2023
7febde0
Shift determinant from LinearTransform to Matrix<T>
Abhinav271828 Sep 29, 2023
05369ca
Implement integer inverse for integer matrix
Abhinav271828 Sep 29, 2023
2273874
Formatting
Abhinav271828 Sep 29, 2023
150b3db
Merge github.com:llvm/llvm-project into upstream
Abhinav271828 Sep 29, 2023
15cfbe0
Revert "Inherit Matrix<Fraction> to FracMatrix and define inverse"
Abhinav271828 Oct 2, 2023
fc689df
Fix reduce and add Fraction tests
Abhinav271828 Oct 2, 2023
8e75c16
Formatting
Abhinav271828 Oct 2, 2023
8431353
Fix CMake file
Abhinav271828 Oct 2, 2023
eebcd40
Fix reduce and add Fraction tests
Abhinav271828 Oct 2, 2023
21c30a9
Formatting
Abhinav271828 Oct 5, 2023
0eb9cfc
Sync up
Abhinav271828 Oct 5, 2023
d51f641
Change assert to expect
Abhinav271828 Oct 6, 2023
aead4ef
Revert "Revert "Inherit Matrix<Fraction> to FracMatrix and define inv…
Abhinav271828 Oct 9, 2023
3494ea4
Shift changes to Fraction
Abhinav271828 Sep 5, 2023
a86c5ee
Update documentation and remove extraneous include
Abhinav271828 Sep 5, 2023
3262c7e
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
d7ab8e3
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
595133f
Add static assert
Abhinav271828 Sep 4, 2023
c3e74b3
Revert "Make compatible with fraction patch"
Abhinav271828 Sep 5, 2023
8935b9c
Remove duplicate definitions
Abhinav271828 Sep 5, 2023
6d3dd31
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
06969a3
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Aug 31, 2023
0fbfd7c
Add static assert
Abhinav271828 Sep 4, 2023
87c5e1a
Fix duplicates and use increment operators for Fraction
Abhinav271828 Sep 6, 2023
f6f11a8
Fix duplicates and delete extra files
Abhinav271828 Sep 6, 2023
e69ffed
Create IntMatrix for hermite normal form and row normalisation
Abhinav271828 Sep 20, 2023
9f640b4
Inherit Matrix<Fraction> to FracMatrix and define inverse
Abhinav271828 Sep 25, 2023
110cad2
Add test for matrix inverse
Abhinav271828 Sep 25, 2023
c2eb9cf
Formatting
Abhinav271828 Sep 26, 2023
942ce0c
Shift determinant from LinearTransform to Matrix<T>
Abhinav271828 Sep 29, 2023
a7ac1ae
Implement integer inverse for integer matrix
Abhinav271828 Sep 29, 2023
c365979
Formatting
Abhinav271828 Sep 29, 2023
016509f
Revert "Inherit Matrix<Fraction> to FracMatrix and define inverse"
Abhinav271828 Oct 2, 2023
4c05825
Formatting
Abhinav271828 Oct 2, 2023
c6ffb37
Fix CMake file
Abhinav271828 Oct 2, 2023
af1fc95
Revert "Revert "Inherit Matrix<Fraction> to FracMatrix and define inv…
Abhinav271828 Oct 9, 2023
9250dff
Fix reduce
Abhinav271828 Oct 9, 2023
7435df4
Template Matrix to Matrix<T> (for MPInt and Fraction) with explicit i…
Abhinav271828 Oct 9, 2023
f4770f3
Sync
Abhinav271828 Oct 9, 2023
a363bf3
Add newlines
Abhinav271828 Oct 9, 2023
396885a
Merge branch 'main' into first_patch
Abhinav271828 Oct 9, 2023
b6d82a2
Use Matrix::identity() for FracMatrix::identity()
Abhinav271828 Oct 9, 2023
3793010
Merge branch 'first_patch' of github.com:Abhinav271828/mlir-barvinok …
Abhinav271828 Oct 9, 2023
664a7d9
Formatting
Abhinav271828 Oct 9, 2023
01112dd
Make inverse functions return optionals
Abhinav271828 Oct 11, 2023
a882857
Revert "Formatting"
Abhinav271828 Oct 13, 2023
9bffad3
Merge branch 'main' into first_patch
Abhinav271828 Oct 13, 2023
f3f3f77
Merge branch 'first_patch' of github.com:Abhinav271828/mlir-barvinok …
Abhinav271828 Oct 13, 2023
a83f2c2
Refactor addToRow call
Abhinav271828 Oct 13, 2023
d231d68
Comparison with integers
Abhinav271828 Oct 13, 2023
f25e3a3
Fix assignment and initialisation in inverse
Abhinav271828 Oct 13, 2023
eb38e98
Inverse documentation
Abhinav271828 Oct 13, 2023
0cdb5f6
Simplify constructors
Abhinav271828 Oct 13, 2023
89a88db
Make determinant recursive
Abhinav271828 Oct 13, 2023
b74f599
Assert square matrix in det
Abhinav271828 Oct 17, 2023
49bbb41
Maintain copy of inverse
Abhinav271828 Oct 17, 2023
912f31d
Formatting
Abhinav271828 Oct 17, 2023
95438b4
Use copy constructor
Abhinav271828 Oct 17, 2023
ae36f7d
Documentation for integerInverse
Abhinav271828 Oct 17, 2023
d99f300
Miscellaneous
Abhinav271828 Oct 17, 2023
24172ce
Refactor determinant and inverse into single function
Abhinav271828 Oct 18, 2023
c69568f
Add cast from IntMatrix to FracMatrix
Abhinav271828 Oct 18, 2023
a566077
Change Matrix attributes to protected
Abhinav271828 Oct 18, 2023
f39f913
Formatting
Abhinav271828 Oct 18, 2023
b6667e8
Fix singularity condition in inverse
Abhinav271828 Oct 18, 2023
bfa0d1d
Merge github.com:llvm/llvm-project into upstream
Abhinav271828 Oct 18, 2023
5e6e3a8
Merge branch 'upstream' into first_patch
Abhinav271828 Oct 18, 2023
f4b1936
Fix MPInt to Fraction conversion
Abhinav271828 Oct 18, 2023
2743884
Fix nonzeroification
Abhinav271828 Oct 18, 2023
0d416e8
Change add to swap
Abhinav271828 Oct 18, 2023
71ce59a
Add test for swapRow
Abhinav271828 Oct 19, 2023
8edddb4
Improve testing
Abhinav271828 Oct 19, 2023
a3270e4
Function to test matrix equality
Abhinav271828 Oct 19, 2023
7f0dcea
Formatting & tidying
Abhinav271828 Oct 19, 2023
16ca081
Formatting
Abhinav271828 Oct 20, 2023
1b377cf
Formatting
Abhinav271828 Oct 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions mlir/include/mlir/Analysis/Presburger/LinearTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ class LinearTransform {
return matrix.postMultiplyWithColumn(colVec);
}

// Compute the determinant of the transform by converting it to row echelon
// form and then taking the product of the diagonal.
MPInt determinant();

private:
IntMatrix matrix;
};
Expand Down
42 changes: 34 additions & 8 deletions mlir/include/mlir/Analysis/Presburger/Matrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class Matrix {
/// invariants satisfied.
bool hasConsistentState() const;

private:
protected:
/// The current number of rows, columns, and reserved columns. The underlying
/// data vector is viewed as an nRows x nReservedColumns matrix, of which the
/// first nColumns columns are currently in use, and the remaining are
Expand All @@ -210,13 +210,7 @@ class IntMatrix : public Matrix<MPInt> {
unsigned reservedColumns = 0)
: Matrix<MPInt>(rows, columns, reservedRows, reservedColumns){};

IntMatrix(Matrix<MPInt> m)
: Matrix<MPInt>(m.getNumRows(), m.getNumColumns(), m.getNumReservedRows(),
m.getNumReservedColumns()) {
for (unsigned i = 0; i < m.getNumRows(); i++)
for (unsigned j = 0; j < m.getNumColumns(); j++)
at(i, j) = m(i, j);
};
IntMatrix(Matrix<MPInt> m) : Matrix<MPInt>(std::move(m)){};

/// Return the identity matrix of the specified dimension.
static IntMatrix identity(unsigned dimension);
Expand All @@ -239,6 +233,38 @@ class IntMatrix : public Matrix<MPInt> {
/// Divide the columns of the specified row by their GCD.
/// Returns the GCD of the columns of the specified row.
MPInt normalizeRow(unsigned row);

// Compute the determinant of the matrix (cubic time).
// Stores the integer inverse of the matrix in the pointer
// passed (if any). The pointer is unchanged if the inverse
// does not exist, which happens iff det = 0.
// For a matrix M, the integer inverse is the matrix M' such that
// M x M' = M'  M = det(M) x I.
// Assert-fails if the matrix is not square.
MPInt determinant(IntMatrix *inverse = nullptr) const;
};

// An inherited class for rational matrices, with no new data attributes.
// This class is for functionality that only applies to matrices of fractions.
class FracMatrix : public Matrix<Fraction> {
public:
FracMatrix(unsigned rows, unsigned columns, unsigned reservedRows = 0,
unsigned reservedColumns = 0)
: Matrix<Fraction>(rows, columns, reservedRows, reservedColumns){};

FracMatrix(Matrix<Fraction> m) : Matrix<Fraction>(std::move(m)){};

explicit FracMatrix(IntMatrix m);

/// Return the identity matrix of the specified dimension.
static FracMatrix identity(unsigned dimension);

// Compute the determinant of the matrix (cubic time).
// Stores the inverse of the matrix in the pointer
// passed (if any). The pointer is unchanged if the inverse
// does not exist, which happens iff det = 0.
// Assert-fails if the matrix is not square.
Fraction determinant(FracMatrix *inverse = nullptr) const;
};

} // namespace presburger
Expand Down
116 changes: 116 additions & 0 deletions mlir/lib/Analysis/Presburger/Matrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,4 +432,120 @@ MPInt IntMatrix::normalizeRow(unsigned row, unsigned cols) {

MPInt IntMatrix::normalizeRow(unsigned row) {
return normalizeRow(row, getNumColumns());
}

MPInt IntMatrix::determinant(IntMatrix *inverse) const {
assert(nRows == nColumns &&
"determinant can only be calculated for square matrices!");

FracMatrix m(*this);

FracMatrix fracInverse(nRows, nColumns);
MPInt detM = m.determinant(&fracInverse).getAsInteger();

if (detM == 0)
return MPInt(0);

*inverse = IntMatrix(nRows, nColumns);
for (unsigned i = 0; i < nRows; i++)
for (unsigned j = 0; j < nColumns; j++)
inverse->at(i, j) = (fracInverse.at(i, j) * detM).getAsInteger();

return detM;
}

FracMatrix FracMatrix::identity(unsigned dimension) {
return Matrix::identity(dimension);
}

FracMatrix::FracMatrix(IntMatrix m)
: FracMatrix(m.getNumRows(), m.getNumColumns()) {
for (unsigned i = 0; i < m.getNumRows(); i++)
for (unsigned j = 0; j < m.getNumColumns(); j++)
this->at(i, j) = m.at(i, j);
}

Fraction FracMatrix::determinant(FracMatrix *inverse) const {
assert(nRows == nColumns &&
"determinant can only be calculated for square matrices!");

FracMatrix m(*this);
FracMatrix tempInv(nRows, nColumns);
if (inverse)
tempInv = FracMatrix::identity(nRows);

Fraction a, b;
// Make the matrix into upper triangular form using
// gaussian elimination with row operations.
// If inverse is required, we apply more operations
// to turn the matrix into diagonal form. We apply
// the same operations to the inverse matrix,
// which is initially identity.
// Either way, the product of the diagonal elements
// is then the determinant.
for (unsigned i = 0; i < nRows; i++) {
if (m(i, i) == 0)
// First ensure that the diagonal
// element is nonzero, by swapping
// it with a nonzero row.
for (unsigned j = i + 1; j < nRows; j++) {
if (m(j, i) != 0) {
m.swapRows(j, i);
if (inverse)
tempInv.swapRows(j, i);
break;
}
}

b = m.at(i, i);
if (b == 0)
return 0;

// Set all elements above the
// diagonal to zero.
if (inverse) {
for (unsigned j = 0; j < i; j++) {
if (m.at(j, i) == 0)
continue;
a = m.at(j, i);
// Set element (j, i) to zero
// by subtracting the ith row,
// appropriately scaled.
m.addToRow(i, j, -a / b);
tempInv.addToRow(i, j, -a / b);
}
}

// Set all elements below the
// diagonal to zero.
for (unsigned j = i + 1; j < nRows; j++) {
if (m.at(j, i) == 0)
continue;
a = m.at(j, i);
// Set element (j, i) to zero
// by subtracting the ith row,
// appropriately scaled.
m.addToRow(i, j, -a / b);
if (inverse)
tempInv.addToRow(i, j, -a / b);
}
}

// Now only diagonal elements of m are nonzero, but they are
// not necessarily 1. To get the true inverse, we should
// normalize them and apply the same scale to the inverse matrix.
// For efficiency we skip scaling m and just scale tempInv appropriately.
if (inverse) {
for (unsigned i = 0; i < nRows; i++)
for (unsigned j = 0; j < nRows; j++)
tempInv.at(i, j) = tempInv.at(i, j) / m(i, i);

*inverse = std::move(tempInv);
}

Fraction determinant = 1;
for (unsigned i = 0; i < nRows; i++)
determinant *= m.at(i, i);

return determinant;
}
74 changes: 68 additions & 6 deletions mlir/unittests/Analysis/Presburger/MatrixTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//

#include "mlir/Analysis/Presburger/Matrix.h"
#include "mlir/Analysis/Presburger/Fraction.h"
#include "./Utils.h"
#include "mlir/Analysis/Presburger/Fraction.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>

Expand Down Expand Up @@ -210,7 +210,8 @@ TEST(MatrixTest, computeHermiteNormalForm) {
{
// Hermite form of a unimodular matrix is the identity matrix.
IntMatrix mat = makeIntMatrix(3, 3, {{2, 3, 6}, {3, 2, 3}, {17, 11, 16}});
IntMatrix hermiteForm = makeIntMatrix(3, 3, {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}});
IntMatrix hermiteForm =
makeIntMatrix(3, 3, {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}});
checkHermiteNormalForm(mat, hermiteForm);
}

Expand Down Expand Up @@ -241,10 +242,71 @@ TEST(MatrixTest, computeHermiteNormalForm) {
}

{
IntMatrix mat =
makeIntMatrix(3, 5, {{0, 2, 0, 7, 1}, {-1, 0, 0, -3, 0}, {0, 4, 1, 0, 8}});
IntMatrix hermiteForm =
makeIntMatrix(3, 5, {{1, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, 0, 1, 0, 0}});
IntMatrix mat = makeIntMatrix(
3, 5, {{0, 2, 0, 7, 1}, {-1, 0, 0, -3, 0}, {0, 4, 1, 0, 8}});
IntMatrix hermiteForm = makeIntMatrix(
3, 5, {{1, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, 0, 1, 0, 0}});
checkHermiteNormalForm(mat, hermiteForm);
}
}

TEST(MatrixTest, inverse) {
FracMatrix mat = makeFracMatrix(
2, 2, {{Fraction(2), Fraction(1)}, {Fraction(7), Fraction(0)}});
FracMatrix inverse = makeFracMatrix(
2, 2, {{Fraction(0), Fraction(1, 7)}, {Fraction(1), Fraction(-2, 7)}});

FracMatrix inv(2, 2);
mat.determinant(&inv);

EXPECT_EQ_FRAC_MATRIX(inv, inverse);

mat = makeFracMatrix(
2, 2, {{Fraction(0), Fraction(1)}, {Fraction(0), Fraction(2)}});
Fraction det = mat.determinant(nullptr);

EXPECT_EQ(det, Fraction(0));

mat = makeFracMatrix(3, 3,
{{Fraction(1), Fraction(2), Fraction(3)},
{Fraction(4), Fraction(8), Fraction(6)},
{Fraction(7), Fraction(8), Fraction(6)}});
inverse = makeFracMatrix(3, 3,
{{Fraction(0), Fraction(-1, 3), Fraction(1, 3)},
{Fraction(-1, 2), Fraction(5, 12), Fraction(-1, 6)},
{Fraction(2, 3), Fraction(-1, 6), Fraction(0)}});

mat.determinant(&inv);
EXPECT_EQ_FRAC_MATRIX(inv, inverse);

mat = makeFracMatrix(0, 0, {});
mat.determinant(&inv);
}

TEST(MatrixTest, intInverse) {
IntMatrix mat = makeIntMatrix(2, 2, {{2, 1}, {7, 0}});
IntMatrix inverse = makeIntMatrix(2, 2, {{0, -1}, {-7, 2}});

IntMatrix inv(2, 2);
mat.determinant(&inv);

EXPECT_EQ_INT_MATRIX(inv, inverse);

mat = makeIntMatrix(
4, 4, {{4, 14, 11, 3}, {13, 5, 14, 12}, {13, 9, 7, 14}, {2, 3, 12, 7}});
inverse = makeIntMatrix(4, 4,
{{155, 1636, -579, -1713},
{725, -743, 537, -111},
{210, 735, -855, 360},
{-715, -1409, 1401, 1482}});

mat.determinant(&inv);

EXPECT_EQ_INT_MATRIX(inv, inverse);

mat = makeIntMatrix(2, 2, {{0, 0}, {1, 2}});

MPInt det = mat.determinant(&inv);

EXPECT_EQ(det, 0);
}
28 changes: 23 additions & 5 deletions mlir/unittests/Analysis/Presburger/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
#define MLIR_UNITTESTS_ANALYSIS_PRESBURGER_UTILS_H

#include "mlir/Analysis/Presburger/IntegerRelation.h"
#include "mlir/Analysis/Presburger/Matrix.h"
#include "mlir/Analysis/Presburger/PWMAFunction.h"
#include "mlir/Analysis/Presburger/PresburgerRelation.h"
#include "mlir/Analysis/Presburger/Simplex.h"
#include "mlir/Analysis/Presburger/Matrix.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/Support/LLVM.h"

Expand All @@ -28,7 +28,7 @@ namespace mlir {
namespace presburger {

inline IntMatrix makeIntMatrix(unsigned numRow, unsigned numColumns,
ArrayRef<SmallVector<int, 8>> matrix) {
ArrayRef<SmallVector<int, 8>> matrix) {
IntMatrix results(numRow, numColumns);
assert(matrix.size() == numRow);
for (unsigned i = 0; i < numRow; ++i) {
Expand All @@ -40,9 +40,9 @@ inline IntMatrix makeIntMatrix(unsigned numRow, unsigned numColumns,
return results;
}

inline Matrix<Fraction> makeFracMatrix(unsigned numRow, unsigned numColumns,
ArrayRef<SmallVector<Fraction, 8>> matrix) {
Matrix<Fraction> results(numRow, numColumns);
inline FracMatrix makeFracMatrix(unsigned numRow, unsigned numColumns,
ArrayRef<SmallVector<Fraction, 8>> matrix) {
FracMatrix results(numRow, numColumns);
assert(matrix.size() == numRow);
for (unsigned i = 0; i < numRow; ++i) {
assert(matrix[i].size() == numColumns &&
Expand All @@ -53,6 +53,24 @@ inline Matrix<Fraction> makeFracMatrix(unsigned numRow, unsigned numColumns,
return results;
}

inline void EXPECT_EQ_INT_MATRIX(IntMatrix a, IntMatrix b) {
EXPECT_EQ(a.getNumRows(), b.getNumRows());
EXPECT_EQ(a.getNumColumns(), b.getNumColumns());

for (unsigned row = 0; row < a.getNumRows(); row++)
for (unsigned col = 0; col < a.getNumColumns(); col++)
EXPECT_EQ(a(row, col), b(row, col));
}

inline void EXPECT_EQ_FRAC_MATRIX(FracMatrix a, FracMatrix b) {
EXPECT_EQ(a.getNumRows(), b.getNumRows());
EXPECT_EQ(a.getNumColumns(), b.getNumColumns());

for (unsigned row = 0; row < a.getNumRows(); row++)
for (unsigned col = 0; col < a.getNumColumns(); col++)
EXPECT_EQ(a(row, col), b(row, col));
}

/// lhs and rhs represent non-negative integers or positive infinity. The
/// infinity case corresponds to when the Optional is empty.
inline bool infinityOrUInt64LE(std::optional<MPInt> lhs,
Expand Down