packages feed

accelerate-examples-0.12.0.0: examples/tests/simple/Radix.hs

{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE ScopedTypeVariables #-}
--
-- Radix sort for a subclass of element types
--

module Radix where

import Random

import Prelude                  as P
import Data.Array.Accelerate    as A

import Data.Bits
import Data.List                ( sort )
import Data.Array.Unboxed       ( IArray, UArray, listArray, bounds, elems )
import System.Random.MWC


-- Radix sort
-- ----------

class Elt e => Radix e where
  passes :: e {- dummy -} -> Int
  radix  :: Exp Int -> Exp e -> Exp Int

instance Radix Int32 where
  passes    = bitSize
  radix i e = i ==* (passes' - 1) ? (radix' (e `xor` minBound), radix' e)
    where
      radix' x = A.fromIntegral $ (x `A.shiftR` i) .&. 1
      passes'  = constant (passes (undefined :: Int32))

-- For IEEE-754 floating-point representation. Unsafe, but widely supported.
-- TLM: unsafeCoerce does not work in the CUDA backend.
--
-- instance Radix Float where
--   passes _  = 32
--   radix i e = let x = (unsafeCoerce e :: Exp Int32)
--               in  i ==* 31 ? (radix' (x `xor` minBound), radix' (floatFlip x))
--     where
--       floatFlip x = x `testBit` 31 ? (complement x, x)  -- twos-complement negative numbers
--       radix'    x = x `testBit` i  ? (1,0)

--
-- A simple (parallel) radix sort implementation [1].
--
-- [1] G. E. Blelloch. "Prefix sums and their applications." Technical Report
--     CMU-CS-90-190. Carnegie Mellon University. 1990.
--
sortAcc :: Radix a => Acc (Vector a) -> Acc (Vector a)
sortAcc = sortAccBy id

sortAccBy
    :: forall a r. (Elt a, Radix r)
    => (Exp a -> Exp r)
    -> Acc (Vector a)
    -> Acc (Vector a)
sortAccBy rdx arr = foldr1 (>->) (P.map radixPass [0..p-1]) arr
  where
    p = passes (undefined :: r)
    --
    deal f x      = let (a,b) = unlift x in (f ==* 0) ? (a,b)
    radixPass k v = let k'    = unit (constant k)
                        flags = A.map (radix (the k') . rdx) v
                        idown = prescanl (+) 0 . A.map (xor 1)        $ flags
                        iup   = A.map (size v - 1 -) . prescanr (+) 0 $ flags
                        index = A.zipWith deal flags (A.zip idown iup)
                    in
                    permute const v (\ix -> index1 (index!ix)) v


sortRef :: (Ord a, IArray UArray a) => UArray Int a -> UArray Int a
sortRef xs = listArray (bounds xs) $ sort (elems xs)


-- Main
-- ----

run :: Int -> IO (() -> UArray Int Int32, () -> Acc (Vector Int32))
run n = withSystemRandom $ \gen -> do
  vec  <- randomUArrayR (minBound,maxBound) gen n
  vec' <- use `fmap` convertUArray vec
  --
  return (run_ref vec, run_acc vec')
  where
    {-# NOINLINE run_ref #-}
    run_ref xs () = sortRef xs
    run_acc xs () = sortAcc xs