Accelerate Transformation Lab Notes - ku-fpg/mandelbrot-examples GitHub Wiki
Givens
abs :: a -> Acc a
rep :: Acc a -> a
-- To simplify talking about transformations in these notes:
dimensions = constant (Z :. width :. height)
(With some type restrictions on abs. See the "Notes on types in Accelerate" for more information on these restrictions).
Transformation
Top-level
generateImage (toRGBF f) w h
=>
genRGBF (interpretResult (run (generate dimensions (indexing2 (abs f)))))
w h
General Transformations
abs (rep x) => x
abs (if b then t else f)
=>
rep (cond (abs b) (abs t) (abs f))
x >= y => rep (abs x >=* abs y)
x + y => rep (abs x + abs y)
Recursion
Idea
Start by turning the recursive function into something that does one step of the recursion. In the general case, something like a CPS transformation will be necessary to turn everything into tail calls (and then lambdas would need to be eliminated somehow), but for the time being we will assume that we only have tail recursion (which is the case in the mandelbrot example).
Concretely,
go k (n, x, y)
| n >= maxIters = (0, 0, 1)
| x*x + y*y >= 4 = (n/maxIters, 0, 0)
| otherwise = go (n+1, (x*x + y*y + scaledX), (2*x*y + scaledY))
becomes
go' k (n, x, y)
| n >= maxIters = (0, 0, 1)
| x*x + y*y >= 4 = (n/maxIters, 0, 0)
| otherwise = k (n+1, (x*x + y*y + scaledX), (2*x*y + scaledY))
go = fix go'
oneStep = abs . go' id . rep -- Note: We might want to write this in a pointful
-- style to avoid dealing with (.) unnecessarily.
(Note: I haven't type checked the definition of oneStep yet. The abs and rep could be in the wrong spot.)
Incidentally, HERMIT already has a command (fix-intro) which will perform this sort of transformation to fix automatically.
This seems very similar to the worker/wrapper transformations.
We can avoid potentially complicated matching-up of base conditions to base cases by running an extra step (this will be shown at the end of the section).
Now, we find all of the conditional branches in the original go where go is not free (since these cannot be recursive if go is not free in them). We can, for the time being, say that these are ultimately "remembered" as a function:
notCond = \(n, x, y) -> (n >= maxIters) || (x*x + y*y >= 4)
I suspect this will be the trickiest part.
Now we should be able to express this at each go call site
... (go initialValue) ...
using the Accelerate while like this
... (oneStep (while (not . notCond)
oneStep
initialValue)) ...
Here, the outer oneStep application calculates the value for the appropriate base case.
If nothing else applies...
abs f => abs (... {inlined f} ...)
Then try to continue with the transformation. If nothing applies again and nothing is of the form abs f where f is an identifier, then terminate. If f cannot be inlined, also terminate.