Matrix inverse is useful when generating code from an affine schedule which must be full-rank (i.e. invertible).
Details
Diff Detail
- Repository
- rZORG LLVM Github Zorg
Event Timeline
Thank you for the patch!
Some comments on coding style to start off. I will put more comments on the algorithms used in a further review.
mlir/include/mlir/Analysis/Presburger/Matrix.h | ||
---|---|---|
48 | ||
48 | Please mark this constructor as explicit. | |
185–196 | Please use full stops at the end of sentences in comments. | |
mlir/lib/Analysis/Presburger/Matrix.cpp | ||
25 | vect[0] here can cause an out-of-bound access if vect is empty. | |
26 | We prefer loops to be written with a pre-evaluated end condition and preincrement. for (unsigned i = 0, e = vect.size(); i < e; ++i) | |
29–31 | Braces can be dropped for single statement loops and if conditions: for (unsigned j = 0; j < vect[0].size(); j++) at(i, j) = vect[i][j]; | |
284 | I don't think "quadratic" makes much sense. "square" seems to be a better term. | |
287–295 | braces can be dropped for single statement if conditions and for loops. | |
299–309 | Please use pre-increment for loops. Also, the loop induction variables should be unsigned. | |
320–324 | Braces can be dropped; use pre-evaluated for loop condition and pre-increment, please. Same for other loops as well. | |
330 | quadratic -> square | |
362 | This is better understood as "Matrix should be unimodular for integer inverse". |
@Groverkss Thanks for the detailed review. gtest does show the algorithm is tending on the expensive side. There was a reason that we went the route of computing the determinant/cofactor as suppose to a method such as LU decomposition. I can find out the precise reason from our scheduler person and update the MR tomorrow. Hope the style better matches your expectation and thanks for catching the zero-row vector bug.
[ RUN ] MatrixTest.determinant [ OK ] MatrixTest.determinant (106 ms) [ RUN ] MatrixTest.transpose [ OK ] MatrixTest.transpose (0 ms) [ RUN ] MatrixTest.cofactor [ OK ] MatrixTest.cofactor (107 ms) [ RUN ] MatrixTest.inverse [ OK ] MatrixTest.inverse (111 ms) [ RUN ] MatrixTest.toVector [ OK ] MatrixTest.toVector (0 ms) [ RUN ] MatrixTest.Matrix [ OK ] MatrixTest.Matrix (108 ms) [----------] 13 tests from MatrixTest (432 ms total)
A simple reason I can see why the computation is expensive is that the determinant algorithm written is exponential, and each call makes a copy instead of creating a view. Fixing this would improve the performance quite a bit, but I would wait for the next round of review. Also, it would be helpful to know why you don't use LU decomposition (or Hermite normal form) before I can review the algorithm.
[ RUN ] MatrixTest.determinant [ OK ] MatrixTest.determinant (106 ms) [ RUN ] MatrixTest.transpose [ OK ] MatrixTest.transpose (0 ms) [ RUN ] MatrixTest.cofactor [ OK ] MatrixTest.cofactor (107 ms) [ RUN ] MatrixTest.inverse [ OK ] MatrixTest.inverse (111 ms) [ RUN ] MatrixTest.toVector [ OK ] MatrixTest.toVector (0 ms) [ RUN ] MatrixTest.Matrix [ OK ] MatrixTest.Matrix (108 ms) [----------] 13 tests from MatrixTest (432 ms total)A simple reason I can see why the computation is expensive is that the determinant algorithm written is exponential, and each call makes a copy instead of creating a view. Fixing this would improve the performance quite a bit, but I would wait for the next round of review. Also, it would be helpful to know why you don't use LU decomposition (or Hermite normal form) before I can review the algorithm.
We need to ensure/check that the determinant of the schedule matrix is always 1, -1 so that the inverse of the schedule can be non-fractional. This mostly stands from that we haven't figured out how to code generate for fractional schedule (which may require introducing if condition inside the newly generated loop to test for non-fraction, or use mod operator) We punted thinking about this to later. This leads us to pick (somewhat carelessly?) a method that implements finding out determinant.
mlir/lib/Analysis/Presburger/Matrix.cpp | ||
---|---|---|
25 | Added a testcase for this. |
From what I understand, you only need the integer inverse of a unimodular matrix. This is better accomplished by Hermite normal form.
The (column) hermite normal form for a matrix M is H = MU, such that U is unimodular, H is lower triangular, and the pivots in H are the max element in that column. Now, det(H) = 1 iff det(M) = 1, since det(U) = 1. det(H) = 1, actually forces H to be the identity matrix and U comes out to be inv(A). So you can simply find the hermite normal form, and use U as inv(A) if det(H) = 1 (since H is lower triangular this is just product of diagonals). Otherwise, M is not unimodular, and we cannot find an integer inverse.
We already have an algorithm to find the hermite normal form of a matrix upstream. I will refactor that algorithm to Matrix, and you can use that directly to get the inverse.
I sent the refactor here: https://reviews.llvm.org/D133510
In pseudo-code, what you would need to do to get the inverse is:
h, u = m.hermiteNormalForm() check if all diagonal elements of h are 1 --> unimodularity check for matrix if yes, return u if no, return nothing since integer inverse does not exist