For every element in every row of the matrix M, first the function f is applied and then the function g.
The parallelization strategy is clearly described here and also the fact that f and g will be computed in two separate steps rather than a single one.
But this leaves the choice of where to allocate the intermediate memory.
On a GPU, there are different address spaces (e.g., local/shared vs global memory) with vastly different size and performance characteristics.
In Lift, this choice is already modeled via specific primitives such as toLocal and toGlobal [1]}, but making this choice explicitly is not enforced.
In the following, we introduce the DPIA language, and its type system that formalizes the intuition that everywhere where we need to write to memory the address space choice has been made.
Furthermore, DPIA combines functional and imperative aspects to facilitate the translation of the optimized functional program to imperative high-performance code.
