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.