I decided to start working on this taking one of the examples I worked out for the book for math Sets (partially incomplete due to Groovy’s inability to directly override lessThan, lessThanEquals, greaterThan, greaterThanEquals) and to put into code some of the techniques I was learning in my Linear Algebra class.
So far I’ve implemented the following into my matrix math DSL:
- Matrix/Row equality – allows you to test if two matrices or rows are equal (i.e. – contain the same ordered set of numbers)
- Matrix addition/subtraction – You can add two matrices together if they are the same dimensions (2×2, 2×3, whatever, as long as the match in row and columns)
- Matrix/Row scalar multiplication – You can multiply a scalar value against a row or matrix
- Matrix multiplication – You can multiply two matrices together if the first matrix A’s (say 2×3) columns match B’s rows of the matrix you are multiplying against (say 3×2) which will produce a 2×2 matrix. If you did BxA then you would end up with a new matrix of 3×3
- Matrix powers – If the matrix is square (say 3×3) you can multiply it by itself to get the power of the matrix
- Left-shift operation – be able to dynamically create a Row or Matrix by adding new elements to the end
I still have a lot of work which is laid out on my Trac site for GroovyMath which leads me to my next conundrum.
When dealing with matrices you need to do a lot of Reduced Echelon Form (REF) or Row Reduced Echelon Form (RREF) this consists of taking one of two Elementary Row Operations (EROs) and applying it to a row to get a matrix in the form of a diagonal (all 1s down the upper-left to lower-right diagonal with 0s everywhere else) or getting it into an upper-diagonal form (0s below the diagonal and integers everywhere else). The two EROs that you can consist of a) row interchange, where you can exchange one row with another; and b) row addition, where you can add one row to another multiplying that row by a scalar. What I would like to be able to do is something like this:
def A = new Matrix(//rows go in here)
A.r1 + A.r2*2 //which will add 2 times the second row and add it to the first and replace it
A.r1 % A.r2 //which will exchange rows 1 and 2 with each other
Another thought I had was to do this in some kind of “rowOperations” closure:
def A = new Matrix(//rows go in here)
A.rowOperations {
r1 + r2*2
r1 % r2
}
Which definitely reads better than the first (and of course that’s all part of a DSL how well does it translate the native tongue into code). But the issue that I have with either of these approaches (maybe, still ironing out the second in my head) is that the properties r1 and r2 don’t exist. These property accessors will need to be intercepted. Ok, not a big deal. But if I choose to implement this in MatrixRow (i.e. – actually return the row itself) it doesn’t allow me to modify the originating matrix (A in this case) only change the row. For the addition problem I think I can get around it as it is executing in a closure, but the row interchange is an issue. The individual rows don’t know they’re a part of a matrix and I’m not sure that knowing it allows them to do anything.
Anyways, just wrestling with this concept right now as it is key to moving on to the more advanced functionality in my matrix math. Any suggestions are more than welcome. One new feature that I really like will be overriding the or operator to provide Augmented Matrices (another key part). Normally you define an augmented matrix in one of three ways:
A|0 – add a column of all zeros
A|b – add a vector as the augmented matrix
A|I – add an Identity matrix
The first two are used to solve the set of linear equations and a bunch of other things. The second is used to find the inverse of the matrix as you transform through EROs the matrix A so that it equals I (the identity matrix) and then the side that “I” was on now equals the inverse of A. I would say stop me if I’m boring you but it’s probably too late for that. I just wanted to point out how cool it was I can override the or operator to do something completely different that makes perfect sense in my given domain.