diff --git a/mlir/include/mlir/Analysis/Presburger/Matrix.h b/mlir/include/mlir/Analysis/Presburger/Matrix.h --- a/mlir/include/mlir/Analysis/Presburger/Matrix.h +++ b/mlir/include/mlir/Analysis/Presburger/Matrix.h @@ -44,6 +44,9 @@ Matrix(unsigned rows, unsigned columns, unsigned reservedRows = 0, unsigned reservedColumns = 0); + /// Construct a matrix from a given vector of vector of integers + Matrix(std::vector> vect); + /// Return the identity matrix of the specified dimension. static Matrix identity(unsigned dimension); @@ -179,6 +182,19 @@ /// invariants satisfied. bool hasConsistentState() const; + /// Return the determinant of the matrix + int64_t determinant() const; + /// Return the transpose of the matrix + Matrix transpose() const; + /// Return the cofactor of the matrix + Matrix cofactor() const; + /// Return the inverse of the matrix, which must be square + /// this method will not work for matrices without integer inverses + Matrix inverse() const; + + /// Return the Matrix as a vector of vector of integers + std::vector> toVector() const; + private: /// The current number of rows, columns, and reserved columns. The underlying /// data vector is viewed as an nRows x nReservedColumns matrix, of which the diff --git a/mlir/lib/Analysis/Presburger/Matrix.cpp b/mlir/lib/Analysis/Presburger/Matrix.cpp --- a/mlir/lib/Analysis/Presburger/Matrix.cpp +++ b/mlir/lib/Analysis/Presburger/Matrix.cpp @@ -21,6 +21,17 @@ data.reserve(std::max(nRows, reservedRows) * nReservedColumns); } +Matrix::Matrix(std::vector> vect) + : Matrix::Matrix(vect.size(), vect[0].size()) { + for (unsigned i = 0; i < vect.size(); i++) { + assert(vect[i].size() == nColumns && + "2d array must be rectangular for conversion to Matrix."); + for (unsigned j = 0; j < vect[0].size(); j++) { + at(i, j) = vect[i][j]; + } + } +} + Matrix Matrix::identity(unsigned dimension) { Matrix matrix(dimension, dimension); for (unsigned i = 0; i < dimension; ++i) @@ -268,3 +279,108 @@ return false; return true; } + +int64_t Matrix::determinant() const { + assert(getNumRows() == getNumColumns() && + "Matrix must be quadratic for determinant."); + + int dimension = getNumRows(); + if (dimension == 0) { + return 1; + } + if (dimension == 1) { + return at(0, 0); + } + if (dimension == 2) { + return at(0, 0) * at(1, 1) - at(0, 1) * at(1, 0); + } + + int64_t result = 0; + int sign = 1; + for (int i = 0; i < dimension; i++) { + Matrix subVect(dimension - 1, dimension - 1); + for (int m = 1; m < dimension; m++) { + int z = 0; + for (int n = 0; n < dimension; n++) { + if (n != i) { + subVect.at(m - 1, z) = at(m, n); + z++; + } + } + } + + result = result + sign * at(0, i) * subVect.determinant(); + sign = -sign; + } + return result; +} + +Matrix Matrix::transpose() const { + Matrix solution(getNumColumns(), getNumRows()); + + for (unsigned i = 0; i < getNumRows(); i++) { + for (unsigned j = 0; j < getNumColumns(); j++) { + solution.at(j, i) = at(i, j); + } + } + return solution; +} + +Matrix Matrix::cofactor() const { + assert(getNumRows() == getNumColumns() && + "Matrix must be quadratic for cofactor."); + + Matrix solution(getNumRows(), getNumColumns()); + Matrix subVect(getNumRows() - 1, getNumColumns() - 1); + + for (unsigned i = 0; i < getNumRows(); i++) { + for (unsigned j = 0; j < getNumRows(); j++) { + int p = 0; + for (unsigned x = 0; x < getNumRows(); x++) { + if (x == i) { + continue; + } + int q = 0; + + for (unsigned y = 0; y < getNumRows(); y++) { + if (y == j) { + continue; + } + + subVect.at(p, q) = at(x, y); + q++; + } + p++; + } + solution.at(i, j) = pow(-1, i + j) * subVect.determinant(); + } + } + return solution; +} + +Matrix Matrix::inverse() const { + int64_t det = determinant(); + assert((det == -1 || det == 1) && "Matrix must have integer inverse."); + + Matrix solution(getNumRows(), getNumColumns()); + solution = cofactor().transpose(); + + for (unsigned i = 0; i < getNumRows(); i++) { + for (unsigned j = 0; j < getNumRows(); j++) { + solution.at(i, j) = solution.at(i, j) * det; + } + } + + return solution; +} + +std::vector> Matrix::toVector() const { + std::vector> result( + getNumRows(), std::vector(getNumColumns())); + for (unsigned i = 0; i < getNumRows(); i++) { + for (unsigned j = 0; j < getNumColumns(); j++) { + result[i][j] = at(i, j); + } + } + return result; +} diff --git a/mlir/unittests/Analysis/Presburger/MatrixTest.cpp b/mlir/unittests/Analysis/Presburger/MatrixTest.cpp --- a/mlir/unittests/Analysis/Presburger/MatrixTest.cpp +++ b/mlir/unittests/Analysis/Presburger/MatrixTest.cpp @@ -191,3 +191,170 @@ for (unsigned col = 0; col < 7; ++col) EXPECT_EQ(mat(row, col), row >= 3 || col >= 3 ? 0 : int(10 * row + col)); } + +TEST(MatrixTest, determinant) { + Matrix mat1(3, 3); + std::vector> test_data1(3, std::vector(3)); + test_data1 = {{1, 99, 3}, {0, 1, 4}, {5, 6, 0}}; + for (unsigned i = 0; i < 3; i++) { + for (unsigned j = 0; j < 3; j++) { + mat1.at(i, j) = test_data1[i][j]; + } + } + EXPECT_EQ(mat1.determinant(), 1941); + + Matrix mat2(4, 4); + std::vector> test_data2(4, std::vector(4)); + test_data2 = {{7, 3, 4, 1}, {0, 1, 1, 0}, {20, 6, 9, 2}, {6, 0, 1, 1}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + mat2.at(i, j) = test_data2[i][j]; + } + } + EXPECT_EQ(mat2.determinant(), 1); + + Matrix mat3(4, 4); + std::vector> test_data3(4, std::vector(4)); + test_data3 = { + {-1, -1, 8, 18}, {1, 2, 4, 4}, {10, 20, 41, 41}, {2, 2, -16, -35}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + mat3.at(i, j) = test_data3[i][j]; + } + } + EXPECT_EQ(mat3.determinant(), -1); + + Matrix mat_nonsquare(2, 1); + std::vector> test_data_nonsquare( + 2, std::vector(1)); + test_data_nonsquare = {{1}, {2}}; + for (unsigned i = 0; i < 2; i++) { + for (unsigned j = 0; j < 1; j++) { + mat_nonsquare.at(i, j) = test_data_nonsquare[i][j]; + } + } + EXPECT_DEATH({ mat_nonsquare.determinant(); }, + "Matrix must be quadratic for determinant."); +} + +TEST(MatrixTest, transpose) { + Matrix mat(4, 2); + std::vector> test_data(4, std::vector(2)); + test_data = {{1, 2}, {3, 4}, {5, 6}, {7, 8}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 2; j++) { + mat.at(i, j) = test_data[i][j]; + } + } + std::vector> test_res(2, std::vector(4)); + test_res = {{1, 3, 5, 7}, {2, 4, 6, 8}}; + for (unsigned i = 0; i < 2; i++) { + for (unsigned j = 0; j < 4; j++) { + EXPECT_EQ(mat.transpose().at(i, j), test_res[i][j]); + } + } +} + +TEST(MatrixTest, cofactor) { + Matrix mat(4, 4); + std::vector> test_data(4, std::vector(4)); + test_data = {{7, 3, 4, 1}, {0, 1, 1, 0}, {20, 6, 9, 2}, {6, 0, 1, 1}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + mat.at(i, j) = test_data[i][j]; + } + } + std::vector> test_res(4, std::vector(4)); + test_res = {{1, 8, -8, 2}, {-3, -17, 18, 0}, {0, -1, 1, -1}, {-1, -6, 6, 1}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + EXPECT_EQ(mat.cofactor().at(i, j), test_res[i][j]); + } + } + + Matrix mat_nonsquare(2, 1); + std::vector> test_data_nonsquare( + 2, std::vector(1)); + test_data_nonsquare = {{1}, {2}}; + for (unsigned i = 0; i < 2; i++) { + for (unsigned j = 0; j < 1; j++) { + mat_nonsquare.at(i, j) = test_data_nonsquare[i][j]; + } + } + EXPECT_DEATH({ mat_nonsquare.cofactor(); }, + "Matrix must be quadratic for cofactor."); +} + +TEST(MatrixTest, inverse) { + Matrix mat1(3, 3); + std::vector> test_data1(3, std::vector(3)); + test_data1 = {{1, 99, 3}, {0, 1, 4}, {5, 6, 0}}; + for (unsigned i = 0; i < 3; i++) { + for (unsigned j = 0; j < 3; j++) { + mat1.at(i, j) = test_data1[i][j]; + } + } + EXPECT_DEATH({ mat1.inverse(); }, "Matrix must have integer inverse."); + + Matrix mat2(4, 4); + std::vector> test_data2(4, std::vector(4)); + test_data2 = {{7, 3, 4, 1}, {0, 1, 1, 0}, {20, 6, 9, 2}, {6, 0, 1, 1}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + mat2.at(i, j) = test_data2[i][j]; + } + } + std::vector> test_res2(4, std::vector(4)); + test_res2 = {{1, -3, 0, -1}, {8, -17, -1, -6}, {-8, 18, 1, 6}, {2, 0, -1, 1}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + EXPECT_EQ(mat2.inverse().at(i, j), test_res2[i][j]); + } + } + + Matrix mat3(4, 4); + std::vector> test_data3(4, std::vector(4)); + test_data3 = { + {-1, -1, 8, 18}, {1, 2, 4, 4}, {10, 20, 41, 41}, {2, 2, -16, -35}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + mat3.at(i, j) = test_data3[i][j]; + } + } + std::vector> test_res3(4, std::vector(4)); + test_res3 = { + {38, -201, 20, 20}, {-19, 121, -12, -10}, {-2, -10, 1, -1}, {2, 0, 0, 1}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + EXPECT_EQ(mat3.inverse().at(i, j), test_res3[i][j]); + } + } +} + +TEST(MatrixTest, toVector) { + Matrix mat(4, 4); + std::vector> test_data(4, std::vector(4)); + test_data = {{7, 3, 4, 1}, {0, 1, 1, 0}, {20, 6, 9, 2}, {6, 0, 1, 1}}; + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + mat.at(i, j) = test_data[i][j]; + } + } + ASSERT_TRUE(test_data == mat.toVector()); +} + +TEST(MatrixTest, Matrix) { + std::vector> test_data1; + test_data1 = {{1, 2, 3, 4}, {1, 2, 3}, {1, 2, 3, 4}}; + EXPECT_DEATH({ Matrix mat1(test_data1); }, + "2d array must be rectangular for conversion to Matrix."); + + std::vector> test_data2(4, std::vector(4)); + test_data2 = {{7, 3, 4, 1}, {0, 1, 1, 0}, {20, 6, 9, 2}, {6, 0, 1, 1}}; + Matrix mat2(test_data2); + for (unsigned i = 0; i < 4; i++) { + for (unsigned j = 0; j < 4; j++) { + EXPECT_EQ(mat2.at(i, j), test_data2[i][j]); + } + } +}