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. + explicit Matrix(const 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,16 @@ data.reserve(std::max(nRows, reservedRows) * nReservedColumns); } +Matrix::Matrix(const std::vector> &vect) + : Matrix::Matrix(vect.size(), vect.size() != 0 ? vect[0].size() : 0) { + for (unsigned i = 0, e = vect.size(); i < e; ++i) { + assert(vect[i].size() == nColumns && + "2d array must be rectangular for conversion to Matrix."); + for (unsigned j = 0, e = vect[0].size(); j < e; ++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 +278,91 @@ return false; return true; } + +int64_t Matrix::determinant() const { + assert(getNumRows() == getNumColumns() && + "Matrix must be square for determinant."); + + unsigned 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 (unsigned i = 0; i < dimension; ++i) { + Matrix subVect(dimension - 1, dimension - 1); + for (unsigned m = 1; m < dimension; ++m) { + int z = 0; + for (unsigned 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 < nRows; ++i) + for (unsigned j = 0; j < nColumns; ++j) + solution.at(j, i) = at(i, j); + return solution; +} + +Matrix Matrix::cofactor() const { + assert(getNumRows() == getNumColumns() && + "Matrix must be square for cofactor."); + + Matrix solution(getNumRows(), getNumColumns()); + Matrix subVect(getNumRows() - 1, getNumColumns() - 1); + for (unsigned i = 0; i < nRows; ++i) { + for (unsigned j = 0; j < nRows; ++j) { + int p = 0; + for (unsigned x = 0; x < nRows; ++x) { + if (x == i) + continue; + int q = 0; + for (unsigned y = 0; y < nRows; ++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 should be unimodular for integer inverse."); + + Matrix solution(getNumRows(), getNumColumns()); + solution = cofactor().transpose(); + for (unsigned i = 0; i < nRows; ++i) + for (unsigned j = 0; j < nRows; ++j) + solution.at(i, j) = solution.at(i, j) * det; + return solution; +} + +std::vector> Matrix::toVector() const { + std::vector> result(nRows, + std::vector(nColumns)); + for (unsigned r = 0; r < nRows; ++r) + for (unsigned c = 0; c < nColumns; ++c) + result[r][c] = at(r, c); + 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,144 @@ 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 square 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 square 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 should be unimodular for 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]); + + std::vector> test_data3; // empty vector. + Matrix mat3(test_data3); + EXPECT_EQ(mat3.getNumRows(), (unsigned)0); + EXPECT_EQ(mat3.getNumColumns(), (unsigned)0); +}