Matrix operators as stack languages 2: closing maps
On the last post we saw how some matrix operations can be considered as forth-like operations over a stack of dimensions.
- To do a swap add a
transpose(mat)
and continue on the left of it. - To move from dimension stack to the auxiliary one, open a
map(
(don't close it) and continue on inside the map. - New operations can be performed inside the
map
as long as they are done over themap
lambda parameter.
Now, let's try to extend the process to allow exiting a MAP (moving from the auxiliary stack back into the dimensions one).
For starters let's update the code from last post so we can see how the operations are converted to code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
If we launch it we can see how the code is built and how the cursor moves:
$ python converter.py 'mat' 'SWAP MAP SWAP MAP SWAP'
Command: SWAP
*CURSOR* | target=transpose(mat)
Command: MAP
map (\next -> *CURSOR* ) (transpose(mat)) | target=next
Command: SWAP
map (\next -> *CURSOR* ) (transpose(mat)) | target=transpose(next)
Command: MAP
map (\next -> map (\next -> *CURSOR* ) (transpose(next)) ) (transpose(mat)) | target=next
Command: SWAP
map (\next -> map (\next -> *CURSOR* ) (transpose(next)) ) (transpose(mat)) | target=transpose(next)
Result: map (\next -> map (\next -> transpose(next) ) (transpose(next)) ) (transpose(mat))
Now, we see that as of now we can't return from a map without completing the whole procedure, as the cursor can only go deeper. For the return to happen we have to use the value of map
we're returning from as the parameter of the following functions.
A minimal example of a program that returned from a map would be something that applied multiple reduce-like operations. For example to sum all elements on different dimensions.
While we're at it, let's remove the map
when applying custom functions (see line 23 above).
:i32.sum \ generates: i32.sum (mat1d)
MAP :i32.sum \ generates: map (i32.sum) (mat2d)
\ Thus, adding a RETurn operation we could do
MAP :i32.sum RET :i32.sum \ generates: i32.sum (map (i32.sum) (mat2d))
\ For 3 dimensions
MAP MAP :i32.sum RET :i32.sum RET :i32.sum
\ generates: i32.sum (map (\next -> i32.sum (map (\next -> (i32.sum) (next))
\ (next)))
\ (mat3d))
\ And finally, for 4 dimensions
MAP MAP MAP :i32.sum RET :i32.sum RET :i32.sum RET :i32.sum
\ generates: i32.sum (map (\next -> (i32.sum (map (\next -> i32.sum (map (\next -> (i32.sum) (next))
\ (next)))
\ (next))))
\ (mat4d))
Note: You might notice in the following script that each innermost map (i32.sum) (mat)
will get converted into a (map (\next -> ((i32.sum) (next)) ) (mat))
. This is equivalent and
allows for more regularity on the converter script.
To do this we might add two elements.
- A cursor stack, which points to the point where the cursor is in before each
map
. This can be done with a simple integer list as the cursor only increases, and nothing is ever added before it. This removes the need for recalculating the map cursors. - Delimiters for
map
scopes. So we can extract a wholemap
operation (and it's parameters) from thetext
list. This has to be implemented as an element inside such list, as elements can be added before the end of the maps, making it necessary to recalculate this positions.
The final script is this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
|
And this is an example of the output:
$ python converter.py 'mat4d' 'MAP MAP MAP :i32.sum RET :i32.sum RET :i32.sum RET :i32.sum'
((i32.sum) ((map (\next -> (((i32.sum) ((map (\next -> (((i32.sum) ((map (\next -> (((i32.sum) (next))) ) (next))))) ) (next))))) ) (mat4d))))
$ futhark repl
[0]> let mat4d = [[[[1,2], [3,4]],[[5,6],[7,8]]],[[[10,11],[12,13]],[[14,15],[16,17]]]]
[1]> ((i32.sum) ((map (\next -> (((i32.sum) ((map (\next -> (((i32.sum) ((map (\next -> (((i32.sum) (next))) ) (next))))) ) (next))))) ) (mat4d))))
144i32
Ok, that looks like enough about this matrix dimension operators, let's go back to the usual :)