{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} module Test.Prelude.Stencil ( test_stencil, ) where import Prelude as P import Data.Label import Data.Maybe import Data.Typeable import Test.QuickCheck import Test.Framework import Test.Framework.Providers.QuickCheck2 import Config import ParseArgs import Test.Base import QuickCheck.Arbitrary.Array () import Data.Array.Accelerate as A import Data.Array.Unboxed as IArray hiding ( Array ) import qualified Data.Array.IArray as IArray -- TODO: -- -- * Tests for boundary conditions: Mirror and Wrap -- * Higher dimensional stencils -- -- -- Stencil --------------------------------------------------------------------- -- test_stencil :: Config -> Test test_stencil opt = testGroup "stencil" $ catMaybes [ testElt configInt8 (undefined :: Int8) , testElt configInt16 (undefined :: Int16) , testElt configInt32 (undefined :: Int32) , testElt configInt64 (undefined :: Int64) , testElt configWord8 (undefined :: Word8) , testElt configWord16 (undefined :: Word16) , testElt configWord32 (undefined :: Word32) , testElt configWord64 (undefined :: Word64) , testElt configFloat (undefined :: Float) , testElt configDouble (undefined :: Double) ] where backend = get configBackend opt testElt :: forall a. (Elt a, IsNum a, Similar a, Arbitrary a, IArray UArray a) => (Config :-> Bool) -> a -> Maybe Test testElt ok _ | P.not (get ok opt) = Nothing | otherwise = Just $ testGroup (show (typeOf (undefined :: a))) [ testProperty "1D" (test_stencil1D :: Array DIM1 a -> Property) , testProperty "2D 3x3 dense" (test_stencil2D1 :: Array DIM2 a -> Property) , testProperty "2D 3x3 cross" (test_stencil2D2 :: Array DIM2 a -> Property) , testProperty "2D non-symmetric" (test_stencil2D3 :: Array DIM2 (a,a) -> Property) ] -- 1D Stencil -- test_stencil1D :: (Num a, IsNum a, Elt a, Similar a, IArray UArray a) => Vector a -> Property test_stencil1D vec = toList (acc vec) ~?= elems (ref (toIArray vec)) where pattern (x,y,z) = x + z - 2 * y acc xs = run backend $ stencil pattern Clamp (use xs) ref :: (Num e, IArray UArray e) => UArray Int e -> UArray Int e ref xs = let (minx,maxx) = bounds xs clamp x = Right (minx `P.max` x `P.min` maxx) in stencil1DRef pattern clamp xs -- 2D Stencil -- test_stencil2D1 :: (Num a, IsNum a, Elt a, Similar a, IArray UArray a) => Array DIM2 a -> Property test_stencil2D1 vec = toList (acc vec) ~?= elems (ref (toIArray vec)) where pattern ( (t1, t2, t3) , (l , m, r ) , (b1, b2, b3) ) = (t1 + t2 + t3 - l + 4*m - r - b1 - b2 - b3) acc xs = run backend $ stencil pattern (Constant 0) (use xs) ref :: (Num a, IArray UArray a) => UArray (Int,Int) a -> UArray (Int,Int) a ref xs = let sh = bounds xs constant ix | inRange sh ix = Right ix | otherwise = Left 0 in stencil2DRef pattern constant xs test_stencil2D2 :: (Num a, IsNum a, Elt a, Similar a, IArray UArray a) => Array DIM2 a -> Property test_stencil2D2 vec = toList (acc vec) ~?= elems (ref (toIArray vec)) where pattern ( (_, t, _) , (l, m, r) , (_, b, _) ) = (t + l + r + b - 4 * m) acc xs = let pattern' :: (Elt a, IsNum a) => Stencil3x3 a -> Exp a pattern' = pattern in run backend $ stencil pattern' Clamp (use xs) ref :: (Num a, IArray UArray a) => UArray (Int,Int) a -> UArray (Int,Int) a ref xs = let ((minu,minv),(maxu,maxv)) = bounds xs clamp (u,v) = Right (minu `P.max` u `P.min` maxu ,minv `P.max` v `P.min` maxv) in stencil2DRef pattern clamp xs test_stencil2D3 :: (Num a, IsNum a, Elt a, Similar a, IArray UArray a) => Array DIM2 (a,a) -> Property test_stencil2D3 vec = toList (acc vec) ~?= elems (ref (toIArray vec)) where pattern :: forall a. (Elt a, IsNum a) => Stencil3x3 (a,a) -> Exp a pattern ( (_, _, _) , (x, _, _) , (y, _, z)) = let (x1,x2) = unlift x (y1,y2) = unlift y (z1,z2) = unlift z in x1 - y2 + y1 - z2 + z1 - x2 pattern' ( (_, _, _) , (x, _, _) , (y, _, z)) = let (x1,x2) = x (y1,y2) = y (z1,z2) = z in x1 - y2 + y1 - z2 + z1 - x2 acc xs = run backend $ stencil pattern (Constant (0,0)) (use xs) ref :: Num a => IArray.Array (Int,Int) (a,a) -> IArray.Array (Int,Int) a ref xs = let sh = bounds xs constant ix | inRange sh ix = Right ix | otherwise = Left (0,0) in stencil2DRef pattern' constant xs -- -- Reference implementation -- stencil1DRef :: (IArray array a, IArray array b) => ((a,a,a) -> b) -> (Int -> Either a Int) -> array Int a -> array Int b stencil1DRef pattern boundary xs = let indexAt ix = case boundary ix of Left e -> e Right ix' -> xs IArray.! ix' f ix = let x = indexAt (ix-1) y = indexAt ix z = indexAt (ix+1) in pattern (x,y,z) in array (bounds xs) [(ix, f ix) | ix <- indices xs] stencil2DRef :: (IArray array a, IArray array b) => (((a,a,a), (a,a,a), (a,a,a)) -> b) -> ((Int,Int) -> Either a (Int,Int)) -> array (Int,Int) a -> array (Int,Int) b stencil2DRef pattern boundary xs = let indexAt ix = case boundary ix of Left e -> e Right ix' -> xs IArray.! ix' f (y,x) = let t1 = indexAt (y-1,x-1) t2 = indexAt (y-1,x ) t3 = indexAt (y-1,x+1) l = indexAt (y, x-1) m = indexAt (y, x ) r = indexAt (y, x+1) b1 = indexAt (y+1,x-1) b2 = indexAt (y+1,x ) b3 = indexAt (y+1,x+1) in pattern ((t1,t2,t3), (l,m,r), (b1,b2,b3)) in array (bounds xs) [(ix, f ix) | ix <- indices xs]