repa 1.1.0.0 → 2.0.0.1
raw patch · 26 files changed
+3002/−844 lines, 26 filesdep +ghc-primdep +template-haskelldep +vectordep −dph-prim-pardep −dph-prim-seqdep ~QuickCheck
Dependencies added: ghc-prim, template-haskell, vector
Dependencies removed: dph-prim-par, dph-prim-seq
Dependency ranges changed: QuickCheck
Files
- Data/Array/Repa.hs +100/−705
- Data/Array/Repa/Arbitrary.hs +99/−0
- Data/Array/Repa/Index.hs +34/−101
- Data/Array/Repa/Internals/Base.hs +389/−0
- Data/Array/Repa/Internals/Elt.hs +163/−0
- Data/Array/Repa/Internals/EvalBlockwise.hs +149/−0
- Data/Array/Repa/Internals/EvalChunked.hs +68/−0
- Data/Array/Repa/Internals/EvalCursored.hs +137/−0
- Data/Array/Repa/Internals/Forcing.hs +190/−0
- Data/Array/Repa/Internals/Gang.hs +248/−0
- Data/Array/Repa/Internals/Select.hs +118/−0
- Data/Array/Repa/Operators/IndexSpace.hs +164/−0
- Data/Array/Repa/Operators/Interleave.hs +110/−0
- Data/Array/Repa/Operators/Mapping.hs +108/−0
- Data/Array/Repa/Operators/Reduction.hs +72/−0
- Data/Array/Repa/Operators/Select.hs +44/−0
- Data/Array/Repa/Operators/Traverse.hs +126/−0
- Data/Array/Repa/Properties.hs +98/−0
- Data/Array/Repa/QuickCheck.hs +0/−21
- Data/Array/Repa/Shape.hs +7/−3
- Data/Array/Repa/Slice.hs +10/−5
- Data/Array/Repa/Specialised/Dim2.hs +109/−0
- Data/Array/Repa/Stencil.hs +271/−0
- Data/Array/Repa/Stencil/Base.hs +59/−0
- Data/Array/Repa/Stencil/Template.hs +99/−0
- repa.cabal +30/−9
Data/Array/Repa.hs view
@@ -1,288 +1,124 @@-{-# LANGUAGE PatternGuards, PackageImports #-}-{-# LANGUAGE ScopedTypeVariables, RankNTypes #-}-{-# LANGUAGE TypeOperators, FlexibleContexts #-}+{-# LANGUAGE PatternGuards, PackageImports, ScopedTypeVariables, RankNTypes #-}+{-# LANGUAGE TypeOperators, FlexibleContexts, NoMonomorphismRestriction, FlexibleInstances, UndecidableInstances #-}+{-# OPTIONS -fno-warn-orphans #-} -- | See the repa-examples package for examples. -- --- More information is also at <http://trac.haskell.org/repa>--- --- NOTE: To get decent performance you must use GHC head branch > 6.13.20100309.------ WARNING: Most of the functions that operate on indices don't perform bounds checks.--- Doing these checks would interfere with code optimisation and reduce performance. --- Indexing outside arrays, or failing to meet the stated obligations will--- likely cause heap corruption.------ +-- More information at <http://trac.haskell.org/repa>+-- module Data.Array.Repa ( module Data.Array.Repa.Shape , module Data.Array.Repa.Index , module Data.Array.Repa.Slice- - , Array (..) - -- * Constructors- , fromUArray- , fromFunction- , unit+ -- from Data.Array.Repa.Internals.Elt -----------------------+ , Elt(..) - -- * Projections- , extent- , delay- , toUArray- , index, (!:)- , toScalar+ -- from Data.Array.Repa.Internals.Base ----------------------+ , Array(..)+ , Region(..)+ , Range(..)+ , Rect(..)+ , Generator(..)+ , deepSeqArray, deepSeqArrays+ , singleton, toScalar+ , extent, delay - -- * Basic Operations- , force- , deepSeqArray- - -- * Conversion+ --+ , withManifest, withManifest'++ -- * Indexing+ , (!), index+ , (!?), safeIndex+ , unsafeIndex++ -- * Construction+ , fromFunction + , fromVector , fromList+ + -- from Data.Array.Repa.Interlals.Forcing -------------------+ -- * Forcing+ , force, force2+ , toVector , toList - -- * Index space transformations+ -- from Data.Array.Repa.Operators.IndexSpace ----------------+ -- * Index space transformations , reshape- , append, (+:+)+ , append, (++) , transpose- , replicate+ , extend , slice , backpermute , backpermuteDft - -- * Structure preserving operations+ -- from Data.Array.Repa.Operators.Mapping -------------------+ -- * Structure preserving operations , map , zipWith+ , (+^), (-^), (*^), (/^) - -- * Reductions+ -- from Data.Array.Repa.Operators.Reduction -----------------+ -- * Reductions , fold, foldAll , sum, sumAll- - -- * Generic traversal++ -- from Data.Array.Repa.Operators.Traverse ------------------+ -- * Generic Traversal , traverse , traverse2 , traverse3 , traverse4- - -- * Interleaving+ , unsafeTraverse+ , unsafeTraverse2+ , unsafeTraverse3+ , unsafeTraverse4++ -- from Data.Array.Repa.Operators.Interleave ----------------+ -- * Interleaving , interleave2 , interleave3 , interleave4+ + -- from Data.Array.Repa.Operators.Select --------------------+ -- * Selection+ , select) - -- * Testing- , arbitrarySmallArray- , props_DataArrayRepa) where import Data.Array.Repa.Index import Data.Array.Repa.Slice import Data.Array.Repa.Shape-import Data.Array.Repa.QuickCheck+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Internals.Forcing+import Data.Array.Repa.Operators.Traverse+import Data.Array.Repa.Operators.IndexSpace+import Data.Array.Repa.Operators.Interleave+import Data.Array.Repa.Operators.Mapping+import Data.Array.Repa.Operators.Reduction+import Data.Array.Repa.Operators.Select import qualified Data.Array.Repa.Shape as S -import "dph-prim-par" Data.Array.Parallel.Unlifted (Elt)-import qualified "dph-prim-par" Data.Array.Parallel.Unlifted as U-import qualified "dph-prim-seq" Data.Array.Parallel.Unlifted.Sequential as USeq--import Test.QuickCheck-import Prelude hiding (sum, map, zipWith, replicate) +import Prelude hiding (sum, map, zipWith, (++)) import qualified Prelude as P stage = "Data.Array.Repa"- --- | Possibly delayed arrays.-data Array sh a- = -- | An array represented as some concrete unboxed data.- Manifest sh (U.Array a) - -- | An array represented as a function that computes each element.- | Delayed sh (sh -> a) --- Constructors -------------------------------------------------------------------------------------- | Create a `Manifest` array from an unboxed `U.Array`. --- The elements are in row-major order.-fromUArray- :: Shape sh- => sh- -> U.Array a- -> Array sh a--{-# INLINE fromUArray #-}-fromUArray sh uarr- = sh `S.deepSeq` - uarr `seq`- Manifest sh uarr----- | Create a `Delayed` array from a function.-fromFunction - :: Shape sh- => sh- -> (sh -> a)- -> Array sh a- -{-# INLINE fromFunction #-}-fromFunction sh fnElems- = sh `S.deepSeq` Delayed sh fnElems----- | Wrap a scalar into a singleton array.-unit :: Elt a => a -> Array Z a-{-# INLINE unit #-}-unit = Delayed Z . const----- Projections ---------------------------------------------------------------------------------------- | Take the extent of an array.-extent :: Array sh a -> sh-{-# INLINE extent #-}-extent arr- = case arr of- Manifest sh _ -> sh- Delayed sh _ -> sh---- | Unpack an array into delayed form.-delay :: (Shape sh, Elt a) - => Array sh a - -> (sh, sh -> a)--{-# INLINE delay #-} -delay arr- = case arr of- Manifest sh uarr -> (sh, \i -> uarr U.!: S.toIndex sh i)- Delayed sh fn -> (sh, fn)----- | Convert an array to an unboxed `U.Array`, forcing it if required.--- The elements come out in row-major order.-toUArray - :: (Shape sh, Elt a)- => Array sh a - -> U.Array a-{-# INLINE toUArray #-}-toUArray arr- = case force arr of- Manifest _ uarr -> uarr- _ -> error $ stage ++ ".toList: force failed"----- | Get an indexed element from an array.------ OBLIGATION: The index must be within the array. ------ @inRange zeroDim (shape arr) ix == True@----index, (!:)- :: forall sh a- . (Shape sh, Elt a)- => Array sh a- -> sh - -> a--{-# INLINE index #-}-index arr ix- = case arr of- Delayed _ fn -> fn ix- Manifest sh uarr -> uarr U.!: (S.toIndex sh ix)--{-# INLINE (!:) #-}-(!:) arr ix = index arr ix----- | Take the scalar value from a singleton array.-toScalar :: Elt a => Array Z a -> a-{-# INLINE toScalar #-}-toScalar arr- = case arr of- Delayed _ fn -> fn Z- Manifest _ uarr -> uarr U.!: 0----- Basic Operations ----------------------------------------------------------------------------------- | Force an array, so that it becomes `Manifest`.-force :: (Shape sh, Elt a)- => Array sh a -> Array sh a- -{-# INLINE force #-}-force arr- = case arr of- Manifest sh uarr- -> sh `S.deepSeq` uarr `seq` - Manifest sh uarr-- Delayed sh fn- -> let uarr = U.map (fn . S.fromIndex sh) - $! U.enumFromTo (0 :: Int) (S.size sh - 1)- in sh `S.deepSeq` uarr `seq`- Manifest sh uarr- - --- | Ensure an array's structure is fully evaluated.--- This evaluates the extent and outer constructor, but does not `force` the elements.-infixr 0 `deepSeqArray`-deepSeqArray - :: Shape sh- => Array sh a - -> b -> b--{-# INLINE deepSeqArray #-}-deepSeqArray arr x - = case arr of- Delayed sh _ -> sh `S.deepSeq` x- Manifest sh uarr -> sh `S.deepSeq` uarr `seq` x----- Conversion ---------------------------------------------------------------------------------------- | Convert a list to an array.--- The length of the list must be exactly the `size` of the extent given, else `error`.----fromList- :: (Shape sh, Elt a)- => sh- -> [a]- -> Array sh a- -{-# INLINE fromList #-}-fromList sh xx- | U.length uarr /= S.size sh- = error $ unlines- [ stage ++ ".fromList: size of array shape does not match size of list"- , " size of shape = " ++ (show $ S.size sh) ++ "\n"- , " size of list = " ++ (show $ U.length uarr) ++ "\n" ]- - | otherwise- = Manifest sh uarr-- where uarr = U.fromList xx- - --- | Convert an array to a list.-toList :: (Shape sh, Elt a)- => Array sh a- -> [a]--{-# INLINE toList #-}-toList arr- = case force arr of- Manifest _ uarr -> U.toList uarr- _ -> error $ stage ++ ".toList: force failed"-- -- Instances -------------------------------------------------------------------------------------- -- Show instance (Shape sh, Elt a, Show a) => Show (Array sh a) where show arr = show $ toList arr + -- Eq instance (Shape sh, Elt a, Eq a) => Eq (Array sh a) where {-# INLINE (==) #-} (==) arr1 arr2 - = toScalar - $ fold (&&) True - $ (flip reshape) (Z :. (S.size $ extent arr1)) + = foldAll (&&) True + $ reshape (Z :. (S.size $ extent arr1)) $ zipWith (==) arr1 arr2 {-# INLINE (/=) #-}@@ -292,496 +128,55 @@ -- Num -- All operators apply elementwise. instance (Shape sh, Elt a, Num a) => Num (Array sh a) where+ {-# INLINE (+) #-} (+) = zipWith (+)++ {-# INLINE (-) #-} (-) = zipWith (-)++ {-# INLINE (*) #-} (*) = zipWith (*)- negate = map negate- abs = map abs- signum = map signum - fromInteger n = Delayed failShape (\_ -> fromInteger n) - where failShape = error $ stage ++ ".fromInteger: Constructed array has no shape."+ {-# INLINE negate #-}+ negate = map negate + {-# INLINE abs #-}+ abs = map abs --- Index space transformations ----------------------------------------------------------------------- | Impose a new shape on the elements of an array.--- The new extent must be the same size as the original, else `error`.----reshape :: (Shape sh, Shape sh', Elt a) - => Array sh a- -> sh'- -> Array sh' a+ {-# INLINE signum #-}+ signum = map signum -{-# INLINE reshape #-}-reshape arr newExtent- | not $ S.size newExtent == S.size (extent arr)- = error $ stage ++ ".reshape: reshaped array will not match size of the original"- - | otherwise- = Delayed newExtent- $ ((arr !:) . (S.fromIndex (extent arr)) . (S.toIndex newExtent))+ {-# INLINE fromInteger #-}+ fromInteger n = fromFunction failShape (\_ -> fromInteger n) + where failShape = error $ stage P.++ ".fromInteger: Constructed array has no shape." --- | Append two arrays.------ OBLIGATION: The higher dimensions of both arrays must have the same extent.------ @tail (listOfShape (shape arr1)) == tail (listOfShape (shape arr2))@----append, (+:+) +-- | Force an array before passing it to a function.+withManifest :: (Shape sh, Elt a)- => Array (sh :. Int) a- -> Array (sh :. Int) a- -> Array (sh :. Int) a--{-# INLINE append #-}-append arr1 arr2 - = traverse2 arr1 arr2 fnExtent fnElem- where- (_ :. n) = extent arr1-- fnExtent (sh :. i) (_ :. j) - = sh :. (i + j)-- fnElem f1 f2 (sh :. i)- | i < n = f1 (sh :. i)- | otherwise = f2 (sh :. (i - n))--{-# INLINE (+:+) #-}-(+:+) arr1 arr2 = append arr1 arr2----- | Transpose the lowest two dimensions of an array. --- Transposing an array twice yields the original.-transpose - :: (Shape sh, Elt a) - => Array (sh :. Int :. Int) a- -> Array (sh :. Int :. Int) a--{-# INLINE transpose #-}-transpose arr - = traverse arr- (\(sh :. m :. n) -> (sh :. n :.m))- (\f -> \(sh :. i :. j) -> f (sh :. j :. i))----- | Replicate an array, according to a given slice specification.-replicate- :: ( Slice sl- , Shape (FullShape sl)- , Shape (SliceShape sl)- , Elt e)- => sl- -> Array (SliceShape sl) e- -> Array (FullShape sl) e--{-# INLINE replicate #-}-replicate sl arr- = backpermute - (fullOfSlice sl (extent arr)) - (sliceOfFull sl)- arr---- | Take a slice from an array, according to a given specification.-slice :: ( Slice sl- , Shape (FullShape sl)- , Shape (SliceShape sl)- , Elt e)- => Array (FullShape sl) e- -> sl- -> Array (SliceShape sl) e--{-# INLINE slice #-}-slice arr sl- = backpermute - (sliceOfFull sl (extent arr))- (fullOfSlice sl)- arr----- | Backwards permutation of an array's elements.--- The result array has the same extent as the original.-backpermute- :: forall sh sh' a- . (Shape sh, Shape sh', Elt a) - => sh' -- ^ Extent of result array.- -> (sh' -> sh) -- ^ Function mapping each index in the result array- -- to an index of the source array.- -> Array sh a -- ^ Source array.- -> Array sh' a+ => (Array sh a -> b) -> Array sh a -> b -{-# INLINE backpermute #-}-backpermute newExtent perm arr- = traverse arr (const newExtent) (. perm) +{-# INLINE withManifest #-}+withManifest f arr+ = case arr of+ Array sh [Region RangeAll (GenManifest vec)]+ -> vec `seq` f (Array sh [Region RangeAll (GenManifest vec)]) ---- | Default backwards permutation of an array's elements.--- If the function returns `Nothing` then the value at that index is taken--- from the default array (@arrDft@)-backpermuteDft- :: forall sh sh' a- . (Shape sh, Shape sh', Elt a) - => Array sh' a -- ^ Default values (@arrDft@)- -> (sh' -> Maybe sh) -- ^ Function mapping each index in the result array- -- to an index in the source array.- -> Array sh a -- ^ Source array.- -> Array sh' a--{-# INLINE backpermuteDft #-}-backpermuteDft arrDft fnIndex arrSrc- = Delayed (extent arrDft) fnElem- where fnElem ix - = case fnIndex ix of- Just ix' -> arrSrc !: ix'- Nothing -> arrDft !: ix- ---- Structure Preserving Operations ------------------------------------------------------------------- | Apply a worker function to each element of an array, --- yielding a new array with the same extent.-map :: (Shape sh, Elt a, Elt b) - => (a -> b)- -> Array sh a- -> Array sh b--{-# INLINE map #-}-map f arr- = Delayed (extent arr) (f . (arr !:))----- | Combine two arrays, element-wise, with a binary operator.--- If the extent of the two array arguments differ, --- then the resulting array's extent is their intersection.-zipWith :: (Shape sh, Elt a, Elt b, Elt c) - => (a -> b -> c) - -> Array sh a- -> Array sh b- -> Array sh c--{-# INLINE zipWith #-}-zipWith f arr1 arr2- = arr1 `deepSeqArray` - arr2 `deepSeqArray`- Delayed (S.intersectDim (extent arr1) (extent arr2))- (\ix -> f (arr1 !: ix) (arr2 !: ix))----- Reductions ----------------------------------------------------------------------------------------- IMPORTANT: --- These reductions use the sequential version of foldU, mapU and enumFromToU.--- If we use parallel versions then we'll end up with nested parallelism--- and the gang will abort at runtime.---- | Fold the innermost dimension of an array.--- Combine this with `transpose` to fold any other dimension.-fold :: (Shape sh, Elt a)- => (a -> a -> a)- -> a - -> Array (sh :. Int) a- -> Array sh a--{-# INLINE fold #-}-fold f x arr- = x `seq` arr `deepSeqArray` - let sh' :. n = extent arr- elemFn i = USeq.foldU f x- $ USeq.mapU- (\ix -> arr !: (i :. ix)) - (USeq.enumFromToU 0 (n - 1))- in Delayed sh' elemFn----- | Fold all the elements of an array.-foldAll :: (Shape sh, Elt a)- => (a -> a -> a)- -> a- -> Array sh a- -> a+ _ -> f (force arr) -{-# INLINE foldAll #-}-foldAll f x arr- = USeq.foldU f x- $ USeq.mapU ((arr !:) . (S.fromIndex (extent arr)))- $ USeq.enumFromToU- 0- ((S.size $ extent arr) - 1) ----- | Sum the innermost dimension of an array.-sum :: (Shape sh, Elt a, Num a)- => Array (sh :. Int) a- -> Array sh a--{-# INLINE sum #-}-sum arr = fold (+) 0 arr----- | Sum all the elements of an array.-sumAll :: (Shape sh, Elt a, Num a)- => Array sh a- -> a--{-# INLINE sumAll #-}-sumAll arr- = USeq.foldU (+) 0- $ USeq.mapU ((arr !:) . (S.fromIndex (extent arr)))- $ USeq.enumFromToU- 0- ((S.size $ extent arr) - 1)----- Generic Traversal -------------------------------------------------------------------------------- | Unstructured traversal.-traverse- :: forall sh sh' a b- . (Shape sh, Shape sh', Elt a)- => Array sh a -- ^ Source array.- -> (sh -> sh') -- ^ Function to produce the extent of the result.- -> ((sh -> a) -> sh' -> b) -- ^ Function to produce elements of the result. - -- It is passed a lookup function to get elements of the source.- -> Array sh' b- -{-# INLINE traverse #-}-traverse arr transExtent newElem- = arr `deepSeqArray`- Delayed - (transExtent (extent arr)) - (newElem (arr !:))----- | Unstructured traversal over two arrays at once.-traverse2- :: forall sh sh' sh'' a b c- . ( Shape sh, Shape sh', Shape sh''- , Elt a, Elt b, Elt c)- => Array sh a -- ^ First source array.- -> Array sh' b -- ^ Second source array.- -> (sh -> sh' -> sh'') -- ^ Function to produce the extent of the result.- -> ((sh -> a) -> (sh' -> b) - -> (sh'' -> c)) -- ^ Function to produce elements of the result.- -- It is passed lookup functions to get elements of the - -- source arrays.- -> Array sh'' c --{-# INLINE traverse2 #-}-traverse2 arrA arrB transExtent newElem- = arrA `deepSeqArray` arrB `deepSeqArray`- Delayed - (transExtent (extent arrA) (extent arrB)) - (newElem ((!:) arrA) ((!:) arrB))----- | Unstructured traversal over three arrays at once.-traverse3- :: forall sh1 sh2 sh3 sh4- a b c d - . ( Shape sh1, Shape sh2, Shape sh3, Shape sh4- , Elt a, Elt b, Elt c, Elt d)- => Array sh1 a - -> Array sh2 b - -> Array sh3 c - -> (sh1 -> sh2 -> sh3 -> sh4) - -> ( (sh1 -> a) -> (sh2 -> b) - -> (sh3 -> c)- -> sh4 -> d ) - -> Array sh4 d--{-# INLINE traverse3 #-}-traverse3 arrA arrB arrC transExtent newElem- = arrA `deepSeqArray` arrB `deepSeqArray` arrC `deepSeqArray`- Delayed - (transExtent (extent arrA) (extent arrB) (extent arrC)) - (newElem (arrA !:) (arrB !:) (arrC !:))----- | Unstructured traversal over four arrays at once.-traverse4- :: forall sh1 sh2 sh3 sh4 sh5 - a b c d e- . ( Shape sh1, Shape sh2, Shape sh3, Shape sh4, Shape sh5- , Elt a, Elt b, Elt c, Elt d, Elt e)- => Array sh1 a - -> Array sh2 b - -> Array sh3 c - -> Array sh4 d - -> (sh1 -> sh2 -> sh3 -> sh4 -> sh5 ) - -> ( (sh1 -> a) -> (sh2 -> b) - -> (sh3 -> c) -> (sh4 -> d)- -> sh5 -> e ) - -> Array sh5 e --{-# INLINE traverse4 #-}-traverse4 arrA arrB arrC arrD transExtent newElem- = arrA `deepSeqArray` arrB `deepSeqArray` arrC `deepSeqArray` arrD `deepSeqArray` - Delayed - (transExtent (extent arrA) (extent arrB) (extent arrC) (extent arrD)) - (newElem (arrA !:) (arrB !:) (arrC !:) (arrD !:))----- Interleaving -------------------------------------------------------------------------------------- | Interleave the elments of two arrays. --- All the input arrays must have the same extent, else `error`.--- The lowest dimenion of the result array is twice the size of the inputs.------ @--- interleave2 a1 a2 b1 b2 => a1 b1 a2 b2--- a3 a4 b3 b4 a3 b3 a4 b4--- @----interleave2+-- | Force an array before passing it to a function.+withManifest' :: (Shape sh, Elt a)- => Array (sh :. Int) a- -> Array (sh :. Int) a- -> Array (sh :. Int) a- -{-# INLINE interleave2 #-}-interleave2 arr1 arr2- = arr1 `deepSeqArray` arr2 `deepSeqArray`- traverse2 arr1 arr2 shapeFn elemFn- where- shapeFn dim1 dim2- | dim1 == dim2- , sh :. len <- dim1- = sh :. (len * 2)- - | otherwise- = error "Data.Array.Repa.interleave2: arrays must have same extent"- - elemFn get1 get2 (sh :. ix)- = case ix `mod` 3 of- 0 -> get1 (sh :. ix `div` 2)- 1 -> get2 (sh :. ix `div` 2)- _ -> error "Data.Array.Repa.interleave2: this never happens :-P"-+ => Array sh a -> (Array sh a -> b) -> b --- | Interleave the elments of three arrays. -interleave3- :: (Shape sh, Elt a)- => Array (sh :. Int) a- -> Array (sh :. Int) a- -> Array (sh :. Int) a- -> Array (sh :. Int) a- -{-# INLINE interleave3 #-}-interleave3 arr1 arr2 arr3- = arr1 `deepSeqArray` arr2 `deepSeqArray` arr3 `deepSeqArray`- traverse3 arr1 arr2 arr3 shapeFn elemFn- where- shapeFn dim1 dim2 dim3- | dim1 == dim2- , dim1 == dim3- , sh :. len <- dim1- = sh :. (len * 3)+{-# INLINE withManifest' #-}+withManifest' arr f+ = case arr of+ Array sh [Region RangeAll (GenManifest vec)]+ -> vec `seq` f (Array sh [Region RangeAll (GenManifest vec)]) - | otherwise- = error "Data.Array.Repa.interleave3: arrays must have same extent"- - elemFn get1 get2 get3 (sh :. ix)- = case ix `mod` 3 of- 0 -> get1 (sh :. ix `div` 3)- 1 -> get2 (sh :. ix `div` 3)- 2 -> get3 (sh :. ix `div` 3)- _ -> error "Data.Array.Repa.interleave3: this never happens :-P"-+ _ -> f (force arr) --- | Interleave the elments of four arrays. -interleave4- :: (Shape sh, Elt a)- => Array (sh :. Int) a- -> Array (sh :. Int) a- -> Array (sh :. Int) a- -> Array (sh :. Int) a- -> Array (sh :. Int) a -{-# INLINE interleave4 #-}-interleave4 arr1 arr2 arr3 arr4- = arr1 `deepSeqArray` arr2 `deepSeqArray` arr3 `deepSeqArray` arr4 `deepSeqArray`- traverse4 arr1 arr2 arr3 arr4 shapeFn elemFn- where- shapeFn dim1 dim2 dim3 dim4- | dim1 == dim2- , dim1 == dim3- , dim1 == dim4- , sh :. len <- dim1- = sh :. (len * 4)- - | otherwise- = error "Data.Array.Repa.interleave4: arrays must have same extent"- - elemFn get1 get2 get3 get4 (sh :. ix)- = case ix `mod` 4 of- 0 -> get1 (sh :. ix `div` 4)- 1 -> get2 (sh :. ix `div` 4)- 2 -> get3 (sh :. ix `div` 4)- 3 -> get4 (sh :. ix `div` 4)- _ -> error "Data.Array.Repa.interleave4: this never happens :-P" ---- Arbitrary ----------------------------------------------------------------------------------------- | Create an arbitrary small array, restricting the size of each of the dimensions to some value.-arbitrarySmallArray - :: (Shape sh, Elt a, Arbitrary sh, Arbitrary a)- => Int- -> Gen (Array (sh :. Int) a)--arbitrarySmallArray maxDim- = do sh <- arbitrarySmallShape maxDim- xx <- arbitraryListOfLength (S.size sh)- return $ fromList sh xx------ Properties ----------------------------------------------------------------------------------------- | QuickCheck properties for this module and its children.-props_DataArrayRepa :: [(String, Property)]-props_DataArrayRepa- = props_DataArrayRepaIndex- ++ [(stage ++ "." ++ name, test) | (name, test)- <- [ ("id_force/DIM5", property prop_id_force_DIM5)- , ("id_toScalarUnit", property prop_id_toScalarUnit)- , ("id_toListFromList/DIM3", property prop_id_toListFromList_DIM3) - , ("id_transpose/DIM4", property prop_id_transpose_DIM4)- , ("reshapeTransposeSize/DIM3", property prop_reshapeTranspose_DIM3)- , ("appendIsAppend/DIM3", property prop_appendIsAppend_DIM3)- , ("sumAllIsSum/DIM3", property prop_sumAllIsSum_DIM3) ]]- ---- The Eq instance uses fold and zipWith.-prop_id_force_DIM5- = forAll (arbitrarySmallArray 10) $ \(arr :: Array DIM5 Int) ->- arr == force arr- -prop_id_toScalarUnit (x :: Int)- = toScalar (unit x) == x---- Conversions -------------------------prop_id_toListFromList_DIM3- = forAll (arbitrarySmallShape 10) $ \(sh :: DIM3) ->- forAll (arbitraryListOfLength (S.size sh)) $ \(xx :: [Int]) ->- toList (fromList sh xx) == xx---- Index Space Transforms --------------prop_id_transpose_DIM4- = forAll (arbitrarySmallArray 20) $ \(arr :: Array DIM3 Int) ->- transpose (transpose arr) == arr---- A reshaped array has the same size and sum as the original-prop_reshapeTranspose_DIM3- = forAll (arbitrarySmallArray 20) $ \(arr :: Array DIM3 Int) ->- let arr' = transpose arr- sh' = extent arr'- in (S.size $ extent arr) == S.size (extent (reshape arr sh'))- && (sumAll arr == sumAll arr')--prop_appendIsAppend_DIM3- = forAll (arbitrarySmallArray 20) $ \(arr1 :: Array DIM3 Int) ->- sumAll (append arr1 arr1) == (2 * sumAll arr1)---- Reductions ---------------------------prop_sumAllIsSum_DIM3- = forAll (arbitrarySmallShape 100) $ \(sh :: DIM2) ->- forAll (arbitraryListOfLength (S.size sh)) $ \(xx :: [Int]) -> - sumAll (fromList sh xx) == P.sum xx
+ Data/Array/Repa/Arbitrary.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE TypeOperators, FlexibleInstances #-}+{-# OPTIONS -fno-warn-orphans #-}++-- Utils to help with testing. Not exported.+module Data.Array.Repa.Arbitrary+ ( arbitraryShape+ , arbitrarySmallShape+ , arbitraryListOfLength+ , arbitrarySmallArray)+where+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Index+import Data.Array.Repa.Shape as S+import Control.Monad+import Test.QuickCheck+++-- Arbitrary --------------------------------------------------------------------------------------+instance Arbitrary Z where+ arbitrary = return Z++-- | Generate an arbitrary index, which may have 0's for some components.+instance (Shape sh, Arbitrary sh) => Arbitrary (sh :. Int) where+ arbitrary + = do sh1 <- arbitrary+ let sh1Unit = if size sh1 == 0 then unitDim else sh1+ + -- Make sure not to create an index so big that we get+ -- integer overflow when converting it to the linear form.+ n <- liftM abs $ arbitrary+ let nMax = maxBound `div` (size sh1Unit)+ let nMaxed = n `mod` nMax+ + return $ sh1 :. nMaxed ++-- | Generate an aribrary shape that does not have 0's for any component.+arbitraryShape + :: (Shape sh, Arbitrary sh) + => Gen (sh :. Int)++arbitraryShape + = do sh1 <- arbitrary+ let sh1Unit = if size sh1 == 0 then unitDim else sh1++ -- Make sure not to create an index so big that we get+ -- integer overflow when converting it to the linear form.+ n <- liftM abs $ arbitrary+ let nMax = maxBound `div` size sh1Unit+ let nMaxed = n `mod` nMax+ let nClamped = if nMaxed == 0 then 1 else nMaxed+ + return $ sh1Unit :. nClamped+ + +-- | Generate an arbitrary shape where each dimension is more than zero, +-- but less than a specific value.+arbitrarySmallShape + :: (Shape sh, Arbitrary sh)+ => Int+ -> Gen (sh :. Int)++arbitrarySmallShape maxDim+ = do sh <- arbitraryShape+ let dims = listOfShape sh++ let clamp x+ = case x `mod` maxDim of+ 0 -> 1+ n -> n+ + return $ if True + then shapeOfList $ map clamp dims+ else sh+++arbitraryListOfLength + :: Arbitrary a+ => Int -> Gen [a]++arbitraryListOfLength n+ | n == 0 = return []+ | otherwise+ = do i <- arbitrary+ rest <- arbitraryListOfLength (n - 1)+ return $ i : rest+ +-- | Create an arbitrary small array, restricting the size of each of the+-- dimensions to some value.+arbitrarySmallArray + :: (Shape sh, Elt a, Arbitrary sh, Arbitrary a)+ => Int+ -> Gen (Array (sh :. Int) a)++arbitrarySmallArray maxDim+ = do sh <- arbitrarySmallShape maxDim+ xx <- arbitraryListOfLength (S.size sh)+ return $ fromList sh xx+
Data/Array/Repa/Index.hs view
@@ -13,16 +13,9 @@ , DIM2 , DIM3 , DIM4- , DIM5 - - -- * Testing- , arbitraryShape- , arbitrarySmallShape- , props_DataArrayRepaIndex)+ , DIM5) where import Data.Array.Repa.Shape-import Test.QuickCheck-import Control.Monad import GHC.Base (quotInt, remInt) stage = "Data.Array.Repa.Index"@@ -48,30 +41,50 @@ -- Shape ------------------------------------------------------------------------------------------ instance Shape Z where- dim _ = 0+ {-# INLINE rank #-}+ rank _ = 0++ {-# INLINE zeroDim #-} zeroDim = Z++ {-# INLINE unitDim #-} unitDim = Z++ {-# INLINE intersectDim #-} intersectDim _ _ = Z + {-# INLINE addDim #-}+ addDim _ _ = Z++ {-# INLINE size #-} size _ = 1++ {-# INLINE sizeIsValid #-} sizeIsValid _ = True ++ {-# INLINE toIndex #-} toIndex _ _ = 0++ {-# INLINE fromIndex #-} fromIndex _ _ = Z - inRange Z Z Z = True + {-# INLINE inShapeRange #-}+ inShapeRange Z Z Z = True+ listOfShape _ = [] shapeOfList [] = Z shapeOfList _ = error $ stage ++ ".fromList: non-empty list when converting to Z." + {-# INLINE deepSeq #-} deepSeq Z x = x instance Shape sh => Shape (sh :. Int) where- {-# INLINE dim #-}- dim (sh :. _)- = dim sh + 1+ {-# INLINE rank #-}+ rank (sh :. _)+ = rank sh + 1 {-# INLINE zeroDim #-} zeroDim = zeroDim :. 0@@ -83,6 +96,10 @@ intersectDim (sh1 :. n1) (sh2 :. n2) = (intersectDim sh1 sh2 :. (min n1 n2)) + {-# INLINE addDim #-}+ addDim (sh1 :. n1) (sh2 :. n2)+ = addDim sh1 sh2 :. (n1 + n2)+ {-# INLINE size #-} size (sh1 :. n) = size sh1 * n@@ -107,12 +124,12 @@ -- in computing the remainder for the highest dimension since -- n < d must hold. This saves one remInt per element access which -- is quite a big deal.- r | dim ds == 0 = n+ r | rank ds == 0 = n | otherwise = n `remInt` d - {-# INLINE inRange #-}- inRange (zs :. z) (sh1 :. n1) (sh2 :. n2) - = (n2 >= z) && (n2 < n1) && (inRange zs sh1 sh2)+ {-# INLINE inShapeRange #-}+ inShapeRange (zs :. z) (sh1 :. n1) (sh2 :. n2) + = (n2 >= z) && (n2 < n1) && (inShapeRange zs sh1 sh2) listOfShape (sh :. n)@@ -129,90 +146,6 @@ --- Arbitrary ---------------------------------------------------------------------------------------instance Arbitrary Z where- arbitrary = return Z---- | Generate an arbitrary index, which may have 0's for some components.-instance (Shape sh, Arbitrary sh) => Arbitrary (sh :. Int) where- arbitrary - = do sh1 <- arbitrary- let sh1Unit = if size sh1 == 0 then unitDim else sh1- - -- Make sure not to create an index so big that we get- -- integer overflow when converting it to the linear form.- n <- liftM abs $ arbitrary- let nMax = maxBound `div` (size sh1Unit)- let nMaxed = n `mod` nMax- - return $ sh1 :. nMaxed ---- | Generate an aribrary shape that does not have 0's for any component.-arbitraryShape - :: (Shape sh, Arbitrary sh) - => Gen (sh :. Int)--arbitraryShape - = do sh1 <- arbitrary- let sh1Unit = if size sh1 == 0 then unitDim else sh1-- -- Make sure not to create an index so big that we get- -- integer overflow when converting it to the linear form.- n <- liftM abs $ arbitrary- let nMax = maxBound `div` size sh1Unit- let nMaxed = n `mod` nMax- let nClamped = if nMaxed == 0 then 1 else nMaxed- - return $ sh1Unit :. nClamped- - --- | Generate an arbitrary shape where each dimension is more than zero, --- but less than a specific value.-arbitrarySmallShape - :: (Shape sh, Arbitrary sh)- => Int- -> Gen (sh :. Int)--arbitrarySmallShape maxDim- = do sh <- arbitraryShape- let dims = listOfShape sh-- let clamp x- = case x `mod` maxDim of- 0 -> 1- n -> n- - return $ if True - then shapeOfList $ map clamp dims- else sh---genInShape2 :: DIM2 -> Gen DIM2-genInShape2 (Z :. yMax :. xMax)- = do y <- liftM (`mod` yMax) $ arbitrary- x <- liftM (`mod` xMax) $ arbitrary- return $ Z :. y :. x----- Properties ---------------------------------------------------------------------------------------- | QuickCheck properties for this module.-props_DataArrayRepaIndex :: [(String, Property)]-props_DataArrayRepaIndex- = [(stage ++ "." ++ name, test) | (name, test)- <- [ ("toIndexFromIndex/DIM1", property prop_toIndexFromIndex_DIM1) - , ("toIndexFromIndex/DIM2", property prop_toIndexFromIndex_DIM2) ]]--prop_toIndexFromIndex_DIM1 sh ix- = (sizeIsValid sh)- ==> (inShape sh ix)- ==> fromIndex sh (toIndex sh ix) == ix- where _types = ( sh :: DIM1- , ix :: DIM1)--prop_toIndexFromIndex_DIM2- = forAll arbitraryShape $ \(sh :: DIM2) ->- forAll (genInShape2 sh) $ \(ix :: DIM2) ->- fromIndex sh (toIndex sh ix) == ix
+ Data/Array/Repa/Internals/Base.hs view
@@ -0,0 +1,389 @@+{-# LANGUAGE ExplicitForAll, TypeOperators, FlexibleInstances, UndecidableInstances, BangPatterns,+ ExistentialQuantification #-}+module Data.Array.Repa.Internals.Base+ ( Array (..)+ , Region(..)+ , Range (..)+ , Rect (..)+ , Generator(..)+ , deepSeqArray, deepSeqArrays++ , singleton, toScalar+ , extent, delay+ + -- * Predicates+ , inRange++ -- * Indexing+ , (!), index+ , (!?), safeIndex+ , unsafeIndex++ -- * Construction+ , fromFunction + , fromVector+ , fromList)+where+import Data.Array.Repa.Index+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Shape as S+import qualified Data.Vector.Unboxed as V+import Data.Vector.Unboxed (Vector)++stage = "Data.Array.Repa.Array"++-- Array ----------------------------------------------------------------------+-- | Repa arrays. +data Array sh a+ = Array + { -- | The entire extent of the array.+ arrayExtent :: sh++ -- | Arrays can be partitioned into several regions.+ , arrayRegions :: [Region sh a] }+++-- | Defines the values in a region of the array.+data Region sh a+ = Region + { -- | The range of elements this region applies to.+ regionRange :: Range sh++ -- | How to compute the array elements in this region.+ , regionGenerator :: Generator sh a }+++-- | Represents a range of elements in the array.+data Range sh + -- | Covers the entire array.+ = RangeAll++ -- | The union of a possibly disjoint set of rectangles.+ | RangeRects+ { rangeMatch :: sh -> Bool+ , rangeRects :: [Rect sh] }+++-- | A rectangle\/cube of arbitrary dimension.+-- The indices are of the minimum and maximim elements to fill.+data Rect sh+ = Rect sh sh++-- | Generates array elements for a particular region in the array.+data Generator sh a+ -- | Elements are already computed and sitting in this vector. + = GenManifest !(Vector a)+ + -- | Elements can be computed using these cursor functions.+ | forall cursor+ . GenCursor+ { -- | Make a cursor to a particular element.+ genMakeCursor :: sh -> cursor+ + -- | Shift the cursor by an offset, to get to another element.+ , genShiftCursor :: sh -> cursor -> cursor++ -- | Load\/compute the element at the given cursor.+ , genLoadElem :: cursor -> a }+++-- DeepSeqs -------------------------------------------------------------------++-- | Ensure the structure for an array is fully evaluated.+-- As we are in a lazy language, applying the @force@ function to a delayed array doesn't+-- actually compute it at that point. Rather, Haskell builds a suspension representing the+-- appliction of the @force@ function to that array. Use @deepSeqArray@ to ensure the array+-- is actually computed at a particular point in the program.+infixr 0 `deepSeqArray`+deepSeqArray :: Shape sh => Array sh a -> b -> b+{-# INLINE deepSeqArray #-}+deepSeqArray (Array ex rgns) x+ = ex `S.deepSeq` rgns `deepSeqRegions` x++-- | Like `deepSeqArray` but seqs all the arrays in a list.+-- This is specialised up to lists of 4 arrays. Using more in the list will break fusion.+infixr 0 `deepSeqArrays`+deepSeqArrays :: Shape sh => [Array sh a] -> b -> b+{-# INLINE deepSeqArrays #-}+deepSeqArrays as y+ = case as of+ [] -> y+ [a] -> a `deepSeqArray` y+ [a1, a2] -> a1 `deepSeqArray` a2 `deepSeqArray` y+ [a1, a2, a3] -> a1 `deepSeqArray` a2 `deepSeqArray` a3 `deepSeqArray` y+ [a1, a2, a3, a4]-> a1 `deepSeqArray` a2 `deepSeqArray` a3 `deepSeqArray` a4 `deepSeqArray` y+ _ -> deepSeqArrays' as y++deepSeqArrays' as' y+ = case as' of+ [] -> y+ x : xs -> x `deepSeqArray` xs `deepSeqArrays` y++-- | Ensure the structure for a region is fully evaluated.+infixr 0 `deepSeqRegion` +deepSeqRegion :: Shape sh => Region sh a -> b -> b+{-# INLINE deepSeqRegion #-}+deepSeqRegion (Region range gen) x+ = range `deepSeqRange` gen `deepSeqGen` x+++-- | Ensure the structure for some regions are fully evaluated.+infixr 0 `deepSeqRegions`+deepSeqRegions :: Shape sh => [Region sh a] -> b -> b+{-# INLINE deepSeqRegions #-}+deepSeqRegions rs y+ = case rs of+ [] -> y+ [r] -> r `deepSeqRegion` y+ [r1, r2] -> r1 `deepSeqRegion` r2 `deepSeqRegion` y+ rs' -> deepSeqRegions' rs' y++deepSeqRegions' rs' y+ = case rs' of+ [] -> y+ x : xs -> x `deepSeqRegion` xs `deepSeqRegions'` y+++-- | Ensure a range is fully evaluated.+infixr 0 `deepSeqRange`+deepSeqRange :: Shape sh => Range sh -> b -> b+{-# INLINE deepSeqRange #-}+deepSeqRange range x+ = case range of+ RangeAll -> x+ RangeRects f rects -> f `seq` rects `seq` x+++-- | Ensure a Generator's structure is fully evaluated.+infixr 0 `deepSeqGen`+deepSeqGen :: Shape sh => Generator sh a -> b -> b+{-# INLINE deepSeqGen #-}+deepSeqGen gen x+ = case gen of+ GenManifest vec -> vec `seq` x+ GenCursor{} -> x+++-- Predicates -------------------------------------------------------------------------------------+inRange :: Shape sh => Range sh -> sh -> Bool+{-# INLINE inRange #-}+inRange RangeAll _ = True+inRange (RangeRects fn _) ix = fn ix+++-- Singletons -------------------------------------------------------------------------------------+-- | Wrap a scalar into a singleton array.+singleton :: Elt a => a -> Array Z a+{-# INLINE singleton #-}+singleton = fromFunction Z . const++-- | Take the scalar value from a singleton array.+toScalar :: Elt a => Array Z a -> a+{-# INLINE toScalar #-}+toScalar arr = arr ! Z+++-- Projections ------------------------------------------------------------------------------------+-- | Take the extent of an array.+extent :: Array sh a -> sh+{-# INLINE extent #-}+extent arr = arrayExtent arr+++-- | Unpack an array into delayed form.+delay :: (Shape sh, Elt a) + => Array sh a + -> (sh, sh -> a)++{-# INLINE delay #-} +delay arr@(Array sh _)+ = (sh, (arr !))+++-- Indexing ---------------------------------------------------------------------------------------+-- | Get an indexed element from an array.+-- This uses the same level of bounds checking as your Data.Vector installation.+(!), index+ :: forall sh a+ . (Shape sh, Elt a)+ => Array sh a+ -> sh + -> a++{-# INLINE (!) #-}+(!) arr ix = index arr ix++{-# INLINE index #-}+index arr ix+ = case arr of+ Array _ []+ -> zero+ + Array sh [Region _ gen1]+ -> indexGen sh gen1 ix+ + Array sh [Region r1 gen1, Region _ gen2]+ | inRange r1 ix -> indexGen sh gen1 ix+ | otherwise -> indexGen sh gen2 ix+ + _ -> index' arr ix+++ where {-# INLINE indexGen #-}+ indexGen sh gen ix'+ = case gen of+ GenManifest vec+ -> vec V.! (S.toIndex sh ix')+ + GenCursor makeCursor _ loadElem+ -> loadElem $ makeCursor ix'++ index' (Array sh (Region range gen : rs)) ix'+ | inRange range ix = indexGen sh gen ix'+ | otherwise = index' (Array sh rs) ix'++ index' (Array _ []) _+ = zero++++-- | Get an indexed element from an array.+-- If the element is out of range then `Nothing`.+(!?), safeIndex+ :: forall sh a+ . (Shape sh, Elt a)+ => Array sh a+ -> sh + -> Maybe a++{-# INLINE (!?) #-}+(!?) arr ix = safeIndex arr ix+++{-# INLINE safeIndex #-}+safeIndex arr ix+ = case arr of+ Array _ []+ -> Nothing+ + Array sh [Region _ gen1]+ -> indexGen sh gen1 ix+ + Array sh [Region r1 gen1, Region r2 gen2]+ | inRange r1 ix -> indexGen sh gen1 ix+ | inRange r2 ix -> indexGen sh gen2 ix+ | otherwise -> Nothing+ + _ -> index' arr ix+++ where {-# INLINE indexGen #-}+ indexGen sh gen ix'+ = case gen of+ GenManifest vec+ -> vec V.!? (S.toIndex sh ix')+ + GenCursor makeCursor _ loadElem+ -> Just (loadElem $ makeCursor ix')++ index' (Array sh (Region range gen : rs)) ix'+ | inRange range ix = indexGen sh gen ix'+ | otherwise = index' (Array sh rs) ix'++ index' (Array _ []) _+ = Nothing+++-- | Get an indexed element from an array, without bounds checking.+-- This assumes that the regions in the array give full coverage.+-- An array with no regions gets zero for every element.+unsafeIndex+ :: forall sh a+ . (Shape sh, Elt a)+ => Array sh a+ -> sh + -> a++{-# INLINE unsafeIndex #-}+unsafeIndex arr ix+ = case arr of+ Array _ []+ -> zero+ + Array sh [Region _ gen1]+ -> unsafeIndexGen sh gen1 ix+ + Array sh [Region r1 gen1, Region _ gen2]+ | inRange r1 ix -> unsafeIndexGen sh gen1 ix+ | otherwise -> unsafeIndexGen sh gen2 ix+ + _ -> unsafeIndex' arr ix++ where {-# INLINE unsafeIndexGen #-}+ unsafeIndexGen sh gen ix'+ = case gen of+ GenManifest vec+ -> vec `V.unsafeIndex` (S.toIndex sh ix')+ + GenCursor makeCursor _ loadElem+ -> loadElem $ makeCursor ix'++ unsafeIndex' (Array sh (Region range gen : rs)) ix'+ | inRange range ix = unsafeIndexGen sh gen ix'+ | otherwise = unsafeIndex' (Array sh rs) ix'++ unsafeIndex' (Array _ []) _+ = zero+ ++-- Conversions ------------------------------------------------------------------------------------+-- | Create a `Delayed` array from a function.+fromFunction + :: Shape sh+ => sh+ -> (sh -> a)+ -> Array sh a+ +{-# INLINE fromFunction #-}+fromFunction sh fnElems+ = sh `S.deepSeq`+ Array sh [Region + RangeAll + (GenCursor id addDim fnElems)]++-- | Create a `Manifest` array from an unboxed `Vector`. +-- The elements are in row-major order.+fromVector+ :: Shape sh+ => sh+ -> Vector a+ -> Array sh a++{-# INLINE fromVector #-}+fromVector sh vec+ = sh `S.deepSeq` vec `seq`+ Array sh [Region RangeAll (GenManifest vec)]+++-- Conversion -------------------------------------------------------------------------------------+-- | Convert a list to an array.+-- The length of the list must be exactly the `size` of the extent given, else `error`.+fromList+ :: (Shape sh, Elt a)+ => sh+ -> [a]+ -> Array sh a+ +{-# INLINE fromList #-}+fromList sh xx+ | V.length vec /= S.size sh+ = error $ unlines+ [ stage ++ ".fromList: size of array shape does not match size of list"+ , " size of shape = " ++ (show $ S.size sh) ++ "\n"+ , " size of list = " ++ (show $ V.length vec) ++ "\n" ]+ + | otherwise+ = Array sh [Region RangeAll (GenManifest vec)]++ where vec = V.fromList xx+
+ Data/Array/Repa/Internals/Elt.hs view
@@ -0,0 +1,163 @@+-- | Values that can be stored in Repa Arrays.+{-# LANGUAGE MagicHash, UnboxedTuples, TypeSynonymInstances, FlexibleInstances #-}+module Data.Array.Repa.Internals.Elt+ (Elt (..))+where+import GHC.Prim+import GHC.Exts+import GHC.Types+import GHC.Word+import GHC.Int+import Data.Vector.Unboxed++ +-- Note that the touch# function is special because we can pass it boxed or unboxed+-- values. The argument type has kind ?, not just * or #.++-- | Element types that can be stored in Repa arrays.+class (Show a, Unbox a) => Elt a where++ -- | We use this to prevent bindings from being floated inappropriatey.+ -- Doing a `seq` sometimes isn't enough, because the GHC simpplifier can + -- erase these, and/or still move around the bindings.+ touch :: a -> IO ()++ -- | Generic zero value, helpful for debugging.+ zero :: a++ -- | Generic one value, helpful for debugging.+ one :: a+++-- Bool -----------------------------------------------------------------------+instance Elt Bool where+ {-# INLINE touch #-}+ touch b+ = IO (\state -> case touch# b state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = False++ {-# INLINE one #-}+ one = True+++-- Tuple ----------------------------------------------------------------------+instance (Elt a, Elt b) => Elt (a, b) where+ {-# INLINE touch #-}+ touch (a, b) + = do touch a+ touch b+ + {-# INLINE zero #-}+ zero = (zero, zero)++ {-# INLINE one #-}+ one = (one, one)+++-- Floating -------------------------------------------------------------------+instance Elt Float where+ {-# INLINE touch #-}+ touch (F# f) + = IO (\state -> case touch# f state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1+++instance Elt Double where+ {-# INLINE touch #-}+ touch (D# d)+ = IO (\state -> case touch# d state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1+++-- Int ------------------------------------------------------------------------+instance Elt Int where+ {-# INLINE touch #-}+ touch (I# i) + = IO (\state -> case touch# i state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1++instance Elt Int8 where+ {-# INLINE touch #-}+ touch (I8# w) + = IO (\state -> case touch# w state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1+++instance Elt Int16 where+ {-# INLINE touch #-}+ touch (I16# w) + = IO (\state -> case touch# w state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1+++-- Word -----------------------------------------------------------------------+instance Elt Word where+ {-# INLINE touch #-}+ touch (W# i) + = IO (\state -> case touch# i state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1+++instance Elt Word8 where+ {-# INLINE touch #-}+ touch (W8# w) + = IO (\state -> case touch# w state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1+++instance Elt Word16 where+ {-# INLINE touch #-}+ touch (W16# w) + = IO (\state -> case touch# w state of+ state' -> (# state', () #))++ {-# INLINE zero #-}+ zero = 0++ {-# INLINE one #-}+ one = 1
+ Data/Array/Repa/Internals/EvalBlockwise.hs view
@@ -0,0 +1,149 @@+{-# LANGUAGE BangPatterns #-}+module Data.Array.Repa.Internals.EvalBlockwise+ ( fillVectorBlockwiseP+ , fillVectorBlock+ , fillVectorBlockP)+where+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Gang+import Data.Vector.Unboxed.Mutable as VM+import GHC.Base (remInt, quotInt)+import Prelude as P+++-- Blockwise filling ------------------------------------------------------------------------------+fillVectorBlockwiseP + :: Elt a+ => IOVector a -- ^ vector to write elements into+ -> (Int -> a) -- ^ fn to evaluate an element at the given index+ -> Int -- ^ width of image.+ -> IO ()+ +{-# INLINE [0] fillVectorBlockwiseP #-}+fillVectorBlockwiseP !vec !getElemFVBP !imageWidth + = gangIO theGang fillBlock+ + where !threads = gangSize theGang+ !vecLen = VM.length vec+ !imageHeight = vecLen `div` imageWidth+ !colChunkLen = imageWidth `quotInt` threads+ !colChunkSlack = imageWidth `remInt` threads++ + {-# INLINE colIx #-}+ colIx !ix+ | ix < colChunkSlack = ix * (colChunkLen + 1)+ | otherwise = ix * colChunkLen + colChunkSlack++ + -- just give one column to each thread+ {-# INLINE fillBlock #-}+ fillBlock :: Int -> IO ()+ fillBlock !ix + = let !x0 = colIx ix + !x1 = colIx (ix + 1)+ !y0 = 0+ !y1 = imageHeight+ in fillVectorBlock vec getElemFVBP imageWidth x0 y0 x1 y1+++-- Block filling ----------------------------------------------------------------------------------+-- | Fill a block in a 2D image, in parallel.+-- Coordinates given are of the filled edges of the block.+-- We divide the block into columns, and give one column to each thread.+fillVectorBlockP+ :: Elt a+ => IOVector a -- ^ vector to write elements into+ -> (Int -> a) -- ^ fn to evaluate an element at the given index.+ -> Int -- ^ width of whole image+ -> Int -- ^ x0 lower left corner of block to fill+ -> Int -- ^ y0 (low x and y value)+ -> Int -- ^ x1 upper right corner of block+ -> Int -- ^ y1 (high x and y value, last index to fill)+ -> IO ()++{-# INLINE [0] fillVectorBlockP #-}+fillVectorBlockP !vec !getElem !imageWidth !x0 !y0 !x1 !y1+ = gangIO theGang fillBlock+ where !threads = gangSize theGang+ !blockWidth = x1 - x0 + 1+ + -- All columns have at least this many pixels.+ !colChunkLen = blockWidth `quotInt` threads++ -- Extra pixels that we have to divide between some of the threads.+ !colChunkSlack = blockWidth `remInt` threads+ + -- Get the starting pixel of a column in the image.+ {-# INLINE colIx #-}+ colIx !ix+ | ix < colChunkSlack = x0 + ix * (colChunkLen + 1)+ | otherwise = x0 + ix * colChunkLen + colChunkSlack+ + -- Give one column to each thread+ {-# INLINE fillBlock #-}+ fillBlock :: Int -> IO ()+ fillBlock !ix+ = let !x0' = colIx ix+ !x1' = colIx (ix + 1) - 1+ !y0' = y0+ !y1' = y1+ in fillVectorBlock vec getElem imageWidth x0' y0' x1' y1'+++-- | Fill a block in a 2D image.+-- Coordinates given are of the filled edges of the block.+fillVectorBlock+ :: Elt a+ => IOVector a -- ^ vector to write elements into.+ -> (Int -> a) -- ^ fn to evaluate an element at the given index.+ -> Int -- ^ width of whole image+ -> Int -- ^ x0 lower left corner of block to fill + -> Int -- ^ y0 (low x and y value)+ -> Int -- ^ x1 upper right corner of block+ -> Int -- ^ y1 (high x and y value, last index to fill)+ -> IO ()++{-# INLINE [0] fillVectorBlock #-}+fillVectorBlock !vec !getElemFVB !imageWidth !x0 !y0 !x1 !y1+ = do -- putStrLn $ "fillVectorBlock: " P.++ show (x0, y0, x1, y1)+ fillBlock ixStart (ixStart + (x1 - x0))+ where + -- offset from end of one line to the start of the next.+ !ixStart = x0 + y0 * imageWidth+ !ixFinal = x1 + y1 * imageWidth+ + {-# INLINE fillBlock #-}+ fillBlock !ixLineStart !ixLineEnd+ | ixLineStart > ixFinal = return ()+ | otherwise+ = do fillLine4 ixLineStart+ fillBlock (ixLineStart + imageWidth) (ixLineEnd + imageWidth)+ + where {-# INLINE fillLine4 #-}+ fillLine4 !ix+ | ix + 4 > ixLineEnd = fillLine1 ix+ | otherwise+ = do+ let d0 = getElemFVB (ix + 0)+ let d1 = getElemFVB (ix + 1)+ let d2 = getElemFVB (ix + 2)+ let d3 = getElemFVB (ix + 3)+ + touch d0+ touch d1+ touch d2+ touch d3+ + VM.unsafeWrite vec (ix + 0) d0+ VM.unsafeWrite vec (ix + 1) d1+ VM.unsafeWrite vec (ix + 2) d2+ VM.unsafeWrite vec (ix + 3) d3+ fillLine4 (ix + 4)++ {-# INLINE fillLine1 #-}+ fillLine1 !ix+ | ix > ixLineEnd = return ()+ | otherwise+ = do VM.unsafeWrite vec ix (getElemFVB ix)+ fillLine1 (ix + 1)
+ Data/Array/Repa/Internals/EvalChunked.hs view
@@ -0,0 +1,68 @@+-- | Evaluate a vector by breaking it up into linear chunks and filling each chunk+-- in parallel.+{-# LANGUAGE BangPatterns #-}+module Data.Array.Repa.Internals.EvalChunked+ ( fillChunkedS+ , fillChunkedP)+where+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Gang+import Data.Vector.Unboxed as V+import Data.Vector.Unboxed.Mutable as VM+import GHC.Base (remInt, quotInt)+import Prelude as P+++-- | Fill a vector sequentially.+fillChunkedS+ :: Elt a+ => IOVector a -- ^ Vector to fill.+ -> (Int -> a) -- ^ Fn to get the value at a given index.+ -> IO ()++{-# INLINE [0] fillChunkedS #-}+fillChunkedS !vec !getElem+ = fill 0+ where !len = VM.length vec+ + fill !ix+ | ix >= len = return ()+ | otherwise+ = do VM.unsafeWrite vec ix (getElem ix)+ fill (ix + 1)+++-- | Fill a vector in parallel.+fillChunkedP+ :: Unbox a+ => IOVector a -- ^ Vector to fill.+ -> (Int -> a) -- ^ Fn to get the value at a given index.+ -> IO ()+ +{-# INLINE [0] fillChunkedP #-}+fillChunkedP !vec !getElem+ = gangIO theGang + $ \thread -> fill (splitIx thread) (splitIx (thread + 1))++ where + -- Decide now to split the work across the threads.+ -- If the length of the vector doesn't divide evenly among the threads,+ -- then the first few get an extra element.+ !threads = gangSize theGang+ !len = VM.length vec+ !chunkLen = len `quotInt` threads+ !chunkLeftover = len `remInt` threads++ {-# INLINE splitIx #-}+ splitIx thread+ | thread < chunkLeftover = thread * (chunkLen + 1)+ | otherwise = thread * chunkLen + chunkLeftover+ + -- Evaluate the elements of a single chunk.+ {-# INLINE fill #-}+ fill !ix !end + | ix >= end = return ()+ | otherwise+ = do VM.unsafeWrite vec ix (getElem ix)+ fill (ix + 1) end+
+ Data/Array/Repa/Internals/EvalCursored.hs view
@@ -0,0 +1,137 @@++{-# LANGUAGE BangPatterns, UnboxedTuples #-}+module Data.Array.Repa.Internals.EvalCursored+ ( fillCursoredBlock2P+ , fillCursoredBlock2 )+where+import Data.Array.Repa.Index+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Gang+import Data.Vector.Unboxed.Mutable as VM+import GHC.Base (remInt, quotInt)+import Prelude as P+++-- Block filling ----------------------------------------------------------------------------------+-- | Fill a block in a 2D image, in parallel.+-- Coordinates given are of the filled edges of the block.+-- We divide the block into columns, and give one column to each thread.+fillCursoredBlock2P+ :: Elt a+ => IOVector a -- ^ vector to write elements into+ -> (DIM2 -> cursor) -- ^ make a cursor to a particular element+ -> (DIM2 -> cursor -> cursor) -- ^ shift the cursor by an offset+ -> (cursor -> a) -- ^ fn to evaluate an element at the given index.+ -> Int -- ^ width of whole image+ -> Int -- ^ x0 lower left corner of block to fill+ -> Int -- ^ y0 (low x and y value)+ -> Int -- ^ x1 upper right corner of block to fill+ -> Int -- ^ y1 (high x and y value, index of last elem to fill)+ -> IO ()++{-# INLINE [0] fillCursoredBlock2P #-}+fillCursoredBlock2P + !vec+ !makeCursorFCB !shiftCursorFCB !getElemFCB+ !imageWidth !x0 !y0 !x1 !y1+ = gangIO theGang fillBlock+ where !threads = gangSize theGang+ !blockWidth = x1 - x0 + 1+ + -- All columns have at least this many pixels.+ !colChunkLen = blockWidth `quotInt` threads++ -- Extra pixels that we have to divide between some of the threads.+ !colChunkSlack = blockWidth `remInt` threads+ + -- Get the starting pixel of a column in the image.+ {-# INLINE colIx #-}+ colIx !ix+ | ix < colChunkSlack = x0 + ix * (colChunkLen + 1)+ | otherwise = x0 + ix * colChunkLen + colChunkSlack+ + -- Give one column to each thread+ {-# INLINE fillBlock #-}+ fillBlock :: Int -> IO ()+ fillBlock !ix+ = let !x0' = colIx ix+ !x1' = colIx (ix + 1) - 1+ !y0' = y0+ !y1' = y1+ in fillCursoredBlock2 + vec + makeCursorFCB shiftCursorFCB getElemFCB+ imageWidth x0' y0' x1' y1'+++-- | Fill a block in a 2D image.+-- Coordinates given are of the filled edges of the block.+fillCursoredBlock2+ :: Elt a+ => IOVector a -- ^ vector to write elements into.+ -> (DIM2 -> cursor) -- ^ make a cursor to a particular element+ -> (DIM2 -> cursor -> cursor) -- ^ shift the cursor by an offset+ -> (cursor -> a) -- ^ fn to evaluate an element at the given index.+ -> Int -- ^ width of whole image+ -> Int -- ^ x0 lower left corner of block to fill + -> Int -- ^ y0 (low x and y value)+ -> Int -- ^ x1 upper right corner of block to fill+ -> Int -- ^ y1 (high x and y value, index of last elem to fill)+ -> IO ()++{-# INLINE [0] fillCursoredBlock2 #-}+fillCursoredBlock2 + !vec + !makeCursor !shiftCursor !getElem+ !imageWidth !x0 !y0 !x1 !y1++ = fillBlock y0++ where {-# INLINE fillBlock #-}+ fillBlock !y+ | y > y1 = return ()+ | otherwise+ = do fillLine4 x0+ fillBlock (y + 1)+ + where {-# INLINE fillLine4 #-}+ fillLine4 !x+ | x + 4 > x1 = fillLine1 x+ | otherwise+ = do -- Compute each source cursor based on the previous one so that+ -- the variable live ranges in the generated code are shorter.+ let srcCur0 = makeCursor (Z :. y :. x)+ let srcCur1 = shiftCursor (Z :. 0 :. 1) srcCur0+ let srcCur2 = shiftCursor (Z :. 0 :. 1) srcCur1+ let srcCur3 = shiftCursor (Z :. 0 :. 1) srcCur2++ -- Get the result value for each cursor.+ let val0 = getElem srcCur0+ let val1 = getElem srcCur1+ let val2 = getElem srcCur2+ let val3 = getElem srcCur3+ + -- Ensure that we've computed each of the result values before we+ -- write into the array. If the backend code generator can't tell+ -- our destination array doesn't alias with the source then writing+ -- to it can prevent the sharing of intermediate computations.+{- touch val0+ touch val1+ touch val2+ touch val3+-}+ -- Compute cursor into destination array.+ let !dstCur0 = x + y * imageWidth + VM.unsafeWrite vec (dstCur0) val0+ VM.unsafeWrite vec (dstCur0 + 1) val1+ VM.unsafeWrite vec (dstCur0 + 2) val2+ VM.unsafeWrite vec (dstCur0 + 3) val3+ fillLine4 (x + 4)+ + {-# INLINE fillLine1 #-}+ fillLine1 !x + | x > x1 = return ()+ | otherwise+ = do VM.unsafeWrite vec (x + y * imageWidth) (getElem $ makeCursor (Z :. y :. x))+ fillLine1 (x + 1)+
+ Data/Array/Repa/Internals/Forcing.hs view
@@ -0,0 +1,190 @@+{-# LANGUAGE BangPatterns #-}+module Data.Array.Repa.Internals.Forcing+ ( toVector+ , toList+ , force, force2)+where+import Data.Array.Repa.Internals.EvalChunked+import Data.Array.Repa.Internals.EvalCursored+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Index+import Data.Array.Repa.Shape as S+import qualified Data.Vector.Unboxed as V+import qualified Data.Vector.Unboxed.Mutable as VM+import Data.Vector.Unboxed (Vector)+import System.IO.Unsafe++stage = "Data.Array.Repa.Internals.Forcing"+++-- Conversions that also force the array ----------------------------------------------------------+-- | Convert an array to an unboxed `Data.Vector`, forcing it if required.+-- The elements come out in row-major order.+toVector+ :: (Shape sh, Elt a)+ => Array sh a + -> Vector a+{-# INLINE toVector #-}+toVector arr+ = case force arr of+ Array _ [Region _ (GenManifest vec)] -> vec+ _ -> error $ stage ++ ".toVector: force failed"+++-- | Convert an array to a list, forcing it if required.+toList :: (Shape sh, Elt a)+ => Array sh a+ -> [a]++{-# INLINE toList #-}+toList arr+ = V.toList $ toVector arr+++-- Forcing ----------------------------------------------------------------------------------------+-- | Force an array, so that it becomes `Manifest`.+-- The array is split into linear chunks and each chunk evaluated in parallel.+force :: (Shape sh, Elt a)+ => Array sh a -> Array sh a++{-# INLINE [2] force #-} +force arr+ = unsafePerformIO+ $ do (sh, vec) <- forceIO arr+ return $ sh `seq` vec `seq` + Array sh [Region RangeAll (GenManifest vec)]+ + where forceIO arr'+ = case arr' of+ -- Don't force an already forced array.+ Array sh [Region RangeAll (GenManifest vec)]+ -> return (sh, vec)++ Array sh _+ -> do mvec <- VM.unsafeNew (S.size sh)+ fillChunkedP mvec (\ix -> arr' `unsafeIndex` fromIndex sh ix)+ vec <- V.unsafeFreeze mvec+ return (sh, vec)+ ++-- | Force an array, so that it becomes `Manifest`.+-- This forcing function is specialised for DIM2 arrays, and does blockwise filling.+force2 :: Elt a => Array DIM2 a -> Array DIM2 a+{-# INLINE [2] force2 #-} +force2 arr+ = unsafePerformIO + $ do (sh, vec) <- forceIO2 arr+ return $ sh `seq` vec `seq` + Array sh [Region RangeAll (GenManifest vec)]++ where forceIO2 arr'+ = arr' `deepSeqArray` + case arr' of+ -- Don't force an already forced array.+ Array sh [Region RangeAll (GenManifest vec)]+ -> return (sh, vec)++ -- Create a vector to hold the new array and load in the regions.+ -- NOTE We must specialise this for the common case of two regions to enable+ -- fusion for them. If we just have the next case (arbitrary region list)+ -- the worker won't fuse with the filling / evaluation code.+ Array sh [r1]+ -> do mvec <- VM.new (S.size sh)+ fillRegion2P mvec sh r1+ vec <- V.unsafeFreeze mvec+ return (sh, vec)++ Array sh [r1, r2]+ -> do mvec <- VM.new (S.size sh)+ fillRegion2P mvec sh r1+ fillRegion2P mvec sh r2+ vec <- V.unsafeFreeze mvec+ return (sh, vec)+ + -- Create a vector to hold the new array and load in the regions.+ Array sh regions+ -> do mvec <- VM.new (S.size sh)+ mapM_ (fillRegion2P mvec sh) regions+ vec <- V.unsafeFreeze mvec+ return (sh, vec)+ ++-- FillRegion2P ----------------------------------------------------------------------------------- +-- | Fill an array region into a vector.+-- This is specialised for DIM2 regions.+-- The region is evaluated in parallel in a blockwise manner, where each block is+-- evaluated independently and in a separate thread. For delayed or cursored regions+-- access their source elements from the local neighbourhood, this specialised version+-- should given better cache performance than plain `fillRegionP`.+--+fillRegion2P + :: Elt a+ => VM.IOVector a -- ^ Vector to write elements into.+ -> DIM2 -- ^ Extent of entire array.+ -> Region DIM2 a -- ^ Region to fill.+ -> IO ()+ +{-# INLINE [1] fillRegion2P #-}+fillRegion2P mvec sh@(_ :. height :. width) (Region range gen)+ = mvec `seq` height `seq` width `seq`+ case range of + RangeAll + -> fillRect2 mvec sh gen + (Rect (Z :. 0 :. 0) + (Z :. height - 1 :. width - 1))++ RangeRects _ [rect]+ -> fillRect2 mvec sh gen rect ++ -- Specialise for the common case of 4 rectangles so we get fusion.+ -- The following case with mapM_ doesn't fuse because mapM_ isn't completely unrolled.+ RangeRects _ [r1, r2, r3, r4]+ -> do fillRect2 mvec sh gen r1+ fillRect2 mvec sh gen r2+ fillRect2 mvec sh gen r3+ fillRect2 mvec sh gen r4++ RangeRects _ rects+ -> mapM_ (fillRect2 mvec sh gen) rects++ +-- | Fill a rectangle in a vector.+fillRect2 + :: Elt a+ => VM.IOVector a -- ^ Vector to write elements into.+ -> DIM2 -- ^ Extent of entire array.+ -> Generator DIM2 a -- ^ Generator for array elements.+ -> Rect DIM2 -- ^ Rectangle to fill.+ -> IO ()++{-# INLINE fillRect2 #-} +fillRect2 mvec (_ :. _ :. width) gen (Rect (Z :. y0 :. x0) (Z :. y1 :. x1)) + = mvec `seq` width `seq` y0 `seq` x0 `seq` y1 `seq` x1 `seq` + case gen of+ GenManifest{}+ -> error "fillRegion2P: GenManifest, copy elements."+ + -- If the region we're filling is just one pixel wide then just fill it+ -- in the current thread instead of starting up the whole gang.+{- GenDelayed getElem+ | x0 == x1+ -> fillVectorBlock mvec+ (getElem . fromIndex sh)+ width x0 y0 x1 y1++ | y0 == y1+ -> fillVectorBlock mvec+ (getElem . fromIndex sh)+ width x0 y0 x1 y1+ + | otherwise+ -> fillVectorBlockP mvec+ (getElem . fromIndex sh) + width x0 y0 x1 y1+-} + -- Cursor based arrays.+ GenCursor makeCursor shiftCursor loadElem+ -> fillCursoredBlock2P mvec+ makeCursor shiftCursor loadElem+ width x0 y0 x1 y1
+ Data/Array/Repa/Internals/Gang.hs view
@@ -0,0 +1,248 @@+{-# LANGUAGE CPP #-}++-- | Gang Primitives.+-- Based on DPH code by Roman Leshchinskiy+-- +-- Gang primitives.+--+#define TRACE_GANG 0++module Data.Array.Repa.Internals.Gang + ( Gang, seqGang, forkGang, gangSize, gangIO, gangST, traceGang, traceGangST+ , theGang)+where+import GHC.IO+import GHC.ST+import GHC.Conc (forkOnIO)++import Control.Concurrent.MVar+import Control.Exception (assert)++import Control.Monad (zipWithM, zipWithM_)+import GHC.Conc (numCapabilities)+import System.IO++#if TRACE_GANG+import GHC.Exts (traceEvent)+import System.Time ( ClockTime(..), getClockTime )+#endif++-- TheGang ----------------------------------------------------------------------------------------+-- | The gang is shared by all computations.+theGang :: Gang+{-# NOINLINE theGang #-}+theGang = unsafePerformIO $ forkGang numCapabilities+++-- Requests ---------------------------------------------------------------------------------------+-- | The 'Req' type encapsulates work requests for individual members of a gang. +data Req + -- | Instruct the worker to run the given action then signal it's done+ -- by writing to the MVar.+ = ReqDo (Int -> IO ()) (MVar ())++ -- | Tell the worker that we're shutting the gang down. The worker should+ -- signal that it's received the equest down by writing to the MVar before+ -- returning to its caller (forkGang) + | ReqShutdown (MVar ())+++-- | Create a new request for the given action.+newReq :: (Int -> IO ()) -> IO Req+newReq p + = do mv <- newEmptyMVar+ return $ ReqDo p mv+++-- | Block until a thread request has been executed.+-- NOTE: only one thread can wait for the request.+waitReq :: Req -> IO ()+waitReq req+ = case req of+ ReqDo _ varDone -> takeMVar varDone+ ReqShutdown varDone -> takeMVar varDone+++-- Gang ------------------------------------------------------------------------------------------+-- | A 'Gang' is a group of threads which execute arbitrary work requests.+-- To get the gang to do work, write Req-uest valuesto its MVars+data Gang + = Gang !Int -- Number of 'Gang' threads+ [MVar Req] -- One 'MVar' per thread+ (MVar Bool) -- Indicates whether the 'Gang' is busy+++instance Show Gang where+ showsPrec p (Gang n _ _) + = showString "<<"+ . showsPrec p n+ . showString " threads>>"+++-- | A sequential gang has no threads.+seqGang :: Gang -> Gang+seqGang (Gang n _ mv) = Gang n [] mv+++-- | The worker thread of a 'Gang'.+-- The threads blocks on the MVar waiting for a work request.+gangWorker :: Int -> MVar Req -> IO ()+gangWorker threadId varReq+ = do traceGang $ "Worker " ++ show threadId ++ " waiting for request."+ req <- takeMVar varReq+ + case req of+ ReqDo action varDone+ -> do traceGang $ "Worker " ++ show threadId ++ " begin"+ start <- getGangTime+ action threadId+ end <- getGangTime+ traceGang $ "Worker " ++ show threadId ++ " end (" ++ diffTime start end ++ ")"+ + putMVar varDone ()+ gangWorker threadId varReq++ ReqShutdown varDone+ -> do traceGang $ "Worker " ++ show threadId ++ " shutting down."+ putMVar varDone ()+++-- | Finaliser for worker threads.+-- We want to shutdown the corresponding thread when it's MVar becomes unreachable.+-- Without this Repa programs can complain about "Blocked indefinitely on an MVar"+-- because worker threads are still blocked on the request MVars when the program ends.+-- Whether the finalizer is called or not is very racey. It happens about 1 in 10 runs+-- when for the repa-edgedetect benchmark, and less often with the others.+-- +-- We're relying on the comment in System.Mem.Weak that says+-- "If there are no other threads to run, the runtime system will check for runnable+-- finalizers before declaring the system to be deadlocked."+-- +-- If we were creating and destroying the gang cleanly we wouldn't need this, but theGang +-- is created with a top-level unsafePerformIO. Hacks beget hacks beget hacks...+--+finaliseWorker :: MVar Req -> IO ()+finaliseWorker varReq+ = do varDone <- newEmptyMVar+ putMVar varReq (ReqShutdown varDone) + takeMVar varDone+ return ()+++-- | Fork a 'Gang' with the given number of threads (at least 1).+forkGang :: Int -> IO Gang+forkGang n+ = assert (n > 0) + $ do + -- Create the vars we'll use to issue work requests.+ mvs <- sequence . replicate n $ newEmptyMVar+ + -- Add finalisers so we can shut the workers down cleanly if they become unreachable.+ mapM_ (\var -> addMVarFinalizer var (finaliseWorker var)) mvs++ -- Create all the worker threads+ zipWithM_ forkOnIO [0..] + $ zipWith gangWorker [0 .. n-1] mvs++ -- The gang is currently idle.+ busy <- newMVar False+ + return $ Gang n mvs busy+++-- | The number of threads in the 'Gang'.+gangSize :: Gang -> Int+gangSize (Gang n _ _) = n+++-- | Issue work requests for the 'Gang' and wait until they have been executed.+-- If the gang is already busy then just run the action in the+-- requesting thread. +--+-- TODO: We might want to print a configurable warning that this is happening.+--+gangIO :: Gang+ -> (Int -> IO ())+ -> IO ()++{-# NOINLINE gangIO #-}+gangIO (Gang n mvs busy) p + = do traceGang "gangIO: issuing work requests (SEQ_IF_GANG_BUSY)"+ b <- swapMVar busy True++ traceGang $ "gangIO: gang is currently " ++ (if b then "busy" else "idle")+ if b+ then do+ hPutStr stderr+ $ unlines [ "Data.Array.Repa: Performing nested parallel computation sequentially."+ , " You've probably called the 'force' function while another instance was"+ , " already running. This can happen if the second version was suspended due"+ , " to lazy evaluation. Use 'deepSeqArray' to ensure that each array is fully"+ , " evaluated before you 'force' the next one."+ , "" ]+ + mapM_ p [0 .. n-1]++ else do+ parIO n mvs p+ _ <- swapMVar busy False+ return ()+++-- | Issue some requests to the worker threads and wait for them to complete.+parIO :: Int -- ^ Number of threads in the gang.+ -> [MVar Req] -- ^ Request vars for worker threads.+ -> (Int -> IO ()) -- ^ Action to run in all the workers, it's given the ix of+ -- the particular worker thread it's running on.+ -> IO ()++parIO n mvs p + = do traceGang "parIO: begin"++ start <- getGangTime+ reqs <- sequence . replicate n $ newReq p++ traceGang "parIO: issuing requests"+ _ <- zipWithM putMVar mvs reqs++ traceGang "parIO: waiting for requests to complete"+ mapM_ waitReq reqs+ end <- getGangTime++ traceGang $ "parIO: end " ++ diffTime start end+++-- | Same as 'gangIO' but in the 'ST' monad.+gangST :: Gang -> (Int -> ST s ()) -> ST s ()+gangST g p = unsafeIOToST . gangIO g $ unsafeSTToIO . p+++-- Tracing ----------------------------------------------------------------------------------------+#if TRACE_GANG+getGangTime :: IO Integer+getGangTime+ = do TOD sec pico <- getClockTime+ return (pico + sec * 1000000000000)++diffTime :: Integer -> Integer -> String+diffTime x y = show (y-x)++traceGang :: String -> IO ()+traceGang s+ = do t <- getGangTime+ traceEvent $ show t ++ " @ " ++ s++#else+getGangTime :: IO ()+getGangTime = return ()++diffTime :: () -> () -> String+diffTime _ _ = ""++traceGang :: String -> IO ()+traceGang _ = return ()++#endif++traceGangST :: String -> ST s ()+traceGangST s = unsafeIOToST (traceGang s)
+ Data/Array/Repa/Internals/Select.hs view
@@ -0,0 +1,118 @@+{-# LANGUAGE BangPatterns, ExplicitForAll, ScopedTypeVariables, PatternGuards #-}+module Data.Array.Repa.Internals.Select+ (selectChunkedS, selectChunkedP)+where+import Data.Array.Repa.Internals.Gang+import Data.Array.Repa.Shape+import Data.Vector.Unboxed as V+import Data.Vector.Unboxed.Mutable as VM+import GHC.Base (remInt, quotInt)+import Prelude as P+import Control.Monad as P+import Data.IORef++-- | Select indices matching a predicate+selectChunkedS + :: (Shape sh, Unbox a)+ => (sh -> Bool) -- ^ See if this predicate matches.+ -> (sh -> a) -- ^ .. and apply fn to the matching index+ -> IOVector a -- ^ .. then write the result into the vector.+ -> sh -- ^ Extent of indices to apply to predicate.+ -> IO Int -- ^ Number of elements written to destination array.++{-# INLINE selectChunkedS #-}+selectChunkedS match produce !vDst !shSize+ = fill 0 0+ where lenSrc = size shSize+ lenDst = VM.length vDst+ + fill !nSrc !nDst+ | nSrc >= lenSrc = return nDst+ | nDst >= lenDst = return nDst+ + | ixSrc <- fromIndex shSize nSrc+ , match ixSrc + = do VM.unsafeWrite vDst nDst (produce ixSrc)+ fill (nSrc + 1) (nDst + 1)++ | otherwise+ = fill (nSrc + 1) nDst+++-- | Select indices matching a predicate, in parallel.+-- The array is chunked up, with one chunk being given to each thread.+-- The number of elements in the result array depends on how many threads +-- you're running the program with.+selectChunkedP + :: forall a+ . Unbox a+ => (Int -> Bool) -- ^ See if this predicate matches.+ -> (Int -> a) -- .. and apply fn to the matching index+ -> Int -- Extent of indices to apply to predicate.+ -> IO [IOVector a] -- Chunks containing array elements.++{-# INLINE selectChunkedP #-}+selectChunkedP !match !produce !len+ = do + -- Make IORefs that the threads will write their result chunks to.+ -- We start with a chunk size proportial to the number of threads we have,+ -- but the threads themselves can grow the chunks if they run out of space.+ refs <- P.replicateM threads + $ do vec <- VM.new $ len `div` threads+ newIORef vec++ -- Fire off a thread to fill each chunk.+ gangIO theGang+ $ \thread -> makeChunk (refs !! thread)+ (splitIx thread)+ (splitIx (thread + 1) - 1)+ + -- Read the result chunks back from the IORefs.+ -- If a thread had to grow a chunk, then these might not be the same ones+ -- we created back in the first step.+ P.mapM readIORef refs+ + where -- See how many threads we have available.+ !threads = gangSize theGang+ !chunkLen = len `quotInt` threads+ !chunkLeftover = len `remInt` threads+++ -- Decide where to split the source array.+ {-# INLINE splitIx #-}+ splitIx thread+ | thread < chunkLeftover = thread * (chunkLen + 1)+ | otherwise = thread * chunkLen + chunkLeftover+++ -- Fill the given chunk with elements selected from this range of indices.+ makeChunk :: IORef (IOVector a) -> Int -> Int -> IO ()+ makeChunk !ref !ixSrc !ixSrcEnd+ = do vecDst <- VM.new (len `div` threads)+ vecDst' <- fillChunk ixSrc ixSrcEnd vecDst 0 (VM.length vecDst - 1) + writeIORef ref vecDst'+++ -- The main filling loop.+ fillChunk :: Int -> Int -> IOVector a -> Int -> Int -> IO (IOVector a)+ fillChunk !ixSrc !ixSrcEnd !vecDst !ixDst !ixDstEnd+ -- If we've finished selecting elements, then slice the vector down + -- so it doesn't have any empty space at the end.+ | ixSrc >= ixSrcEnd + = return $ VM.slice 0 ixDst vecDst+ + -- If we've run out of space in the chunk then grow it some more.+ | ixDst >= ixDstEnd+ = do let ixDstEnd' = VM.length vecDst * 2 - 1+ vecDst' <- VM.grow vecDst (ixDstEnd + 1)+ fillChunk (ixSrc + 1) ixSrcEnd vecDst' (ixDst + 1) ixDstEnd'+ + -- We've got a maching element, so add it to the chunk.+ | match ixSrc+ = do VM.unsafeWrite vecDst ixDst (produce ixSrc)+ fillChunk (ixSrc + 1) ixSrcEnd vecDst (ixDst + 1) ixDstEnd+ + -- The element doesnt match, so keep going.+ | otherwise+ = fillChunk (ixSrc + 1) ixSrcEnd vecDst ixDst ixDstEnd+
+ Data/Array/Repa/Operators/IndexSpace.hs view
@@ -0,0 +1,164 @@+{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE TypeOperators, ExplicitForAll, FlexibleContexts #-}++module Data.Array.Repa.Operators.IndexSpace+ ( reshape+ , append, (++)+ , transpose+ , extend+ , slice+ , backpermute+ , backpermuteDft)+where+import Data.Array.Repa.Index+import Data.Array.Repa.Slice+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Operators.Traverse+import Data.Array.Repa.Shape as S+import Prelude hiding ((++))+import qualified Prelude as P++stage = "Data.Array.Repa.Operators.IndexSpace"++-- Index space transformations --------------------------------------------------------------------+-- | Impose a new shape on the elements of an array.+-- The new extent must be the same size as the original, else `error`.+-- +-- TODO: This only works for arrays with a single region. +-- +reshape :: (Shape sh, Shape sh', Elt a) + => sh'+ -> Array sh a+ -> Array sh' a++{-# INLINE reshape #-}+reshape sh' arr+ | not $ S.size sh' == S.size (extent arr)+ = error $ stage P.++ ".reshape: reshaped array will not match size of the original"++reshape sh' (Array sh [Region RangeAll gen])+ = Array sh' [Region RangeAll gen']+ where gen' = case gen of+ GenManifest vec + -> GenManifest vec++ GenCursor makeCursor _ loadElem+ -> GenCursor + id+ addDim+ (loadElem . makeCursor . fromIndex sh . toIndex sh')++reshape _ _+ = error $ stage P.++ ".reshape: can't reshape a partitioned array"+ ++-- | Append two arrays.+--+append, (++) + :: (Shape sh, Elt a)+ => Array (sh :. Int) a+ -> Array (sh :. Int) a+ -> Array (sh :. Int) a++{-# INLINE append #-}+append arr1 arr2 + = unsafeTraverse2 arr1 arr2 fnExtent fnElem+ where+ (_ :. n) = extent arr1++ fnExtent (sh :. i) (_ :. j) + = sh :. (i + j)++ fnElem f1 f2 (sh :. i)+ | i < n = f1 (sh :. i)+ | otherwise = f2 (sh :. (i - n))++{-# INLINE (++) #-}+(++) arr1 arr2 = append arr1 arr2+++-- | Transpose the lowest two dimensions of an array. +-- Transposing an array twice yields the original.+transpose + :: (Shape sh, Elt a) + => Array (sh :. Int :. Int) a+ -> Array (sh :. Int :. Int) a++{-# INLINE transpose #-}+transpose arr + = unsafeTraverse arr+ (\(sh :. m :. n) -> (sh :. n :.m))+ (\f -> \(sh :. i :. j) -> f (sh :. j :. i))+++-- | Extend an array, according to a given slice specification.+-- (used to be called replicate).+extend+ :: ( Slice sl+ , Shape (FullShape sl)+ , Shape (SliceShape sl)+ , Elt e)+ => sl+ -> Array (SliceShape sl) e+ -> Array (FullShape sl) e++{-# INLINE extend #-}+extend sl arr+ = backpermute + (fullOfSlice sl (extent arr)) + (sliceOfFull sl)+ arr++-- | Take a slice from an array, according to a given specification.+slice :: ( Slice sl+ , Shape (FullShape sl)+ , Shape (SliceShape sl)+ , Elt e)+ => Array (FullShape sl) e+ -> sl+ -> Array (SliceShape sl) e++{-# INLINE slice #-}+slice arr sl+ = backpermute + (sliceOfFull sl (extent arr))+ (fullOfSlice sl)+ arr+++-- | Backwards permutation of an array's elements.+-- The result array has the same extent as the original.+backpermute+ :: forall sh sh' a+ . (Shape sh, Shape sh', Elt a) + => sh' -- ^ Extent of result array.+ -> (sh' -> sh) -- ^ Function mapping each index in the result array+ -- to an index of the source array.+ -> Array sh a -- ^ Source array.+ -> Array sh' a++{-# INLINE backpermute #-}+backpermute newExtent perm arr+ = traverse arr (const newExtent) (. perm) + ++-- | Default backwards permutation of an array's elements.+-- If the function returns `Nothing` then the value at that index is taken+-- from the default array (@arrDft@)+backpermuteDft+ :: forall sh sh' a+ . (Shape sh, Shape sh', Elt a) + => Array sh' a -- ^ Default values (@arrDft@)+ -> (sh' -> Maybe sh) -- ^ Function mapping each index in the result array+ -- to an index in the source array.+ -> Array sh a -- ^ Source array.+ -> Array sh' a++{-# INLINE backpermuteDft #-}+backpermuteDft arrDft fnIndex arrSrc+ = fromFunction (extent arrDft) fnElem+ where fnElem ix + = case fnIndex ix of+ Just ix' -> arrSrc ! ix'+ Nothing -> arrDft ! ix
+ Data/Array/Repa/Operators/Interleave.hs view
@@ -0,0 +1,110 @@+{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE TypeOperators, PatternGuards #-}++module Data.Array.Repa.Operators.Interleave+ ( interleave2+ , interleave3+ , interleave4)+where+import Data.Array.Repa.Index+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Operators.Traverse+import Data.Array.Repa.Shape as S++-- | Interleave the elements of two arrays. +-- All the input arrays must have the same extent, else `error`.+-- The lowest dimenion of the result array is twice the size of the inputs.+--+-- @+-- interleave2 a1 a2 b1 b2 => a1 b1 a2 b2+-- a3 a4 b3 b4 a3 b3 a4 b4+-- @+--+interleave2+ :: (Shape sh, Elt a)+ => Array (sh :. Int) a+ -> Array (sh :. Int) a+ -> Array (sh :. Int) a+ +{-# INLINE interleave2 #-}+interleave2 arr1 arr2+ = arr1 `deepSeqArray` arr2 `deepSeqArray`+ unsafeTraverse2 arr1 arr2 shapeFn elemFn+ where+ shapeFn dim1 dim2+ | dim1 == dim2+ , sh :. len <- dim1+ = sh :. (len * 2)+ + | otherwise+ = error "Data.Array.Repa.interleave2: arrays must have same extent"+ + elemFn get1 get2 (sh :. ix)+ = case ix `mod` 3 of+ 0 -> get1 (sh :. ix `div` 2)+ 1 -> get2 (sh :. ix `div` 2)+ _ -> error "Data.Array.Repa.interleave2: this never happens :-P"+++-- | Interleave the elements of three arrays. +interleave3+ :: (Shape sh, Elt a)+ => Array (sh :. Int) a+ -> Array (sh :. Int) a+ -> Array (sh :. Int) a+ -> Array (sh :. Int) a+ +{-# INLINE interleave3 #-}+interleave3 arr1 arr2 arr3+ = arr1 `deepSeqArray` arr2 `deepSeqArray` arr3 `deepSeqArray`+ unsafeTraverse3 arr1 arr2 arr3 shapeFn elemFn+ where+ shapeFn dim1 dim2 dim3+ | dim1 == dim2+ , dim1 == dim3+ , sh :. len <- dim1+ = sh :. (len * 3)+ + | otherwise+ = error "Data.Array.Repa.interleave3: arrays must have same extent"+ + elemFn get1 get2 get3 (sh :. ix)+ = case ix `mod` 3 of+ 0 -> get1 (sh :. ix `div` 3)+ 1 -> get2 (sh :. ix `div` 3)+ 2 -> get3 (sh :. ix `div` 3)+ _ -> error "Data.Array.Repa.interleave3: this never happens :-P"+++-- | Interleave the elements of four arrays. +interleave4+ :: (Shape sh, Elt a)+ => Array (sh :. Int) a+ -> Array (sh :. Int) a+ -> Array (sh :. Int) a+ -> Array (sh :. Int) a+ -> Array (sh :. Int) a+ +{-# INLINE interleave4 #-}+interleave4 arr1 arr2 arr3 arr4+ = arr1 `deepSeqArray` arr2 `deepSeqArray` arr3 `deepSeqArray` arr4 `deepSeqArray`+ unsafeTraverse4 arr1 arr2 arr3 arr4 shapeFn elemFn+ where+ shapeFn dim1 dim2 dim3 dim4+ | dim1 == dim2+ , dim1 == dim3+ , dim1 == dim4+ , sh :. len <- dim1+ = sh :. (len * 4)+ + | otherwise+ = error "Data.Array.Repa.interleave4: arrays must have same extent"+ + elemFn get1 get2 get3 get4 (sh :. ix)+ = case ix `mod` 4 of+ 0 -> get1 (sh :. ix `div` 4)+ 1 -> get2 (sh :. ix `div` 4)+ 2 -> get3 (sh :. ix `div` 4)+ 3 -> get4 (sh :. ix `div` 4)+ _ -> error "Data.Array.Repa.interleave4: this never happens :-P"
+ Data/Array/Repa/Operators/Mapping.hs view
@@ -0,0 +1,108 @@+{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE NoMonomorphismRestriction, PatternGuards #-}++module Data.Array.Repa.Operators.Mapping+ ( map+ , zipWith+ , (+^)+ , (-^)+ , (*^)+ , (/^))+where+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Shape as S+import qualified Data.Vector.Unboxed as V+import qualified Prelude as P+import Prelude (($), (.), (+), (*), (+), (/), (-))++-- | Apply a worker function to each element of an array, yielding a new array with the same extent.+--+-- This is specialised for arrays of up to four regions, using more breaks fusion.+--+map :: (Shape sh, Elt a, Elt b) + => (a -> b)+ -> Array sh a+ -> Array sh b++{-# INLINE map #-}+map f (Array sh regions)+ = Array sh (mapRegions regions)++ where {-# INLINE mapRegions #-}+ mapRegions rs+ = case rs of+ [] -> []+ [r] -> [mapRegion r]+ [r1, r2] -> [mapRegion r1, mapRegion r2]+ [r1, r2, r3] -> [mapRegion r1, mapRegion r2, mapRegion r3]+ [r1, r2, r3, r4] -> [mapRegion r1, mapRegion r2, mapRegion r3, mapRegion r4]+ _ -> mapRegions' rs+ + mapRegions' rs+ = case rs of+ [] -> []+ (r : rs') -> mapRegion r : mapRegions' rs'+ + {-# INLINE mapRegion #-}+ mapRegion (Region range gen)+ = Region range (mapGen gen)+ + {-# INLINE mapGen #-}+ mapGen gen+ = case gen of+ GenManifest vec+ -> GenCursor+ P.id+ addDim+ (\ix -> f $ V.unsafeIndex vec $ S.toIndex sh ix)+ + GenCursor makeCursor shiftCursor loadElem+ -> GenCursor makeCursor shiftCursor (f . loadElem)+++-- | Combine two arrays, element-wise, with a binary operator.+-- If the extent of the two array arguments differ, +-- then the resulting array's extent is their intersection.+--+zipWith :: (Shape sh, Elt a, Elt b, Elt c) + => (a -> b -> c) + -> Array sh a+ -> Array sh b+ -> Array sh c++{-# INLINE zipWith #-}+zipWith f arr1 arr2+ | Array sh2 [_] <- arr1+ , Array sh1 [ Region g21 (GenCursor make21 _ load21)+ , Region g22 (GenCursor make22 _ load22)] <- arr2++ = let {-# INLINE load21' #-}+ load21' ix = f (arr1 `unsafeIndex` ix) (load21 $ make21 ix)++ {-# INLINE load22' #-}+ load22' ix = f (arr1 `unsafeIndex` ix) (load22 $ make22 ix)+ + in Array (S.intersectDim sh1 sh2)+ [ Region g21 (GenCursor P.id addDim load21')+ , Region g22 (GenCursor P.id addDim load22') ]++ | P.otherwise+ = let {-# INLINE getElem' #-}+ getElem' ix = f (arr1 `unsafeIndex` ix) (arr2 `unsafeIndex` ix)+ in fromFunction+ (S.intersectDim (extent arr1) (extent arr2))+ getElem'+++{-# INLINE (+^) #-}+(+^) = zipWith (+)++{-# INLINE (-^) #-}+(-^) = zipWith (-)++{-# INLINE (*^) #-}+(*^) = zipWith (*)++{-# INLINE (/^) #-}+(/^) = zipWith (/)
+ Data/Array/Repa/Operators/Reduction.hs view
@@ -0,0 +1,72 @@+{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE ExplicitForAll, TypeOperators #-}++module Data.Array.Repa.Operators.Reduction+ ( fold, foldAll+ , sum, sumAll)+where+import Data.Array.Repa.Index+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Shape as S+import qualified Data.Vector.Unboxed as V+import Prelude hiding (sum)+++-- | Sequentially fold the innermost dimension of an array.+-- Combine this with `transpose` to fold any other dimension.+fold :: (Shape sh, Elt a)+ => (a -> a -> a)+ -> a + -> Array (sh :. Int) a+ -> Array sh a++{-# INLINE fold #-}+fold f x arr+ = x `seq` arr `deepSeqArray` + let sh' :. n = extent arr+ elemFn i = V.foldl' f x+ $ V.map (\ix -> arr ! (i :. ix)) + (V.enumFromTo 0 (n - 1))+ in fromFunction sh' elemFn+++-- | Sequentially fold all the elements of an array.+foldAll :: (Shape sh, Elt a)+ => (a -> a -> a)+ -> a+ -> Array sh a+ -> a+ +{-# INLINE foldAll #-}+foldAll f x arr+ = V.foldl' f x+ $ V.map ((arr !) . (S.fromIndex (extent arr)))+ $ V.enumFromTo+ 0+ ((S.size $ extent arr) - 1)++++-- | Sum the innermost dimension of an array.+sum :: (Shape sh, Elt a, Num a)+ => Array (sh :. Int) a+ -> Array sh a++{-# INLINE sum #-}+sum arr = fold (+) 0 arr+++-- | Sum all the elements of an array.+sumAll :: (Shape sh, Elt a, Num a)+ => Array sh a+ -> a++{-# INLINE sumAll #-}+sumAll arr+ = V.foldl' (+) 0+ $ V.map ((arr !) . (S.fromIndex (extent arr)))+ $ V.enumFromTo+ 0+ ((S.size $ extent arr) - 1)+
+ Data/Array/Repa/Operators/Select.hs view
@@ -0,0 +1,44 @@+{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE BangPatterns #-}++module Data.Array.Repa.Operators.Select+ (select)+where+import Data.Array.Repa.Index+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Internals.Select+import qualified Data.Vector.Unboxed as V+import System.IO.Unsafe+++-- | Produce an array by applying a predicate to a range of integers.+-- If the predicate matches, then use the second function to generate+-- the element.+--+-- This is a low-level function helpful for writing filtering operations on arrays. +-- Use the integer as the index into the array you're filtering.+--+select :: Elt a+ => (Int -> Bool) -- ^ If the Int matches this predicate, + -> (Int -> a) -- ^ ... then pass it to this fn to produce a value+ -> Int -- ^ Range between 0 and this maximum.+ -> Array DIM1 a -- ^ Array containing produced values.+ +{-# INLINE select #-}+select match produce len+ = unsafePerformIO + $ do (sh, vec) <- selectIO + return $ sh `seq` vec `seq` + Array sh [Region RangeAll (GenManifest vec)]+ + where {-# INLINE selectIO #-}+ selectIO+ = do vecs <- selectChunkedP match produce len+ vecs' <- mapM V.unsafeFreeze vecs++ -- TODO: avoid copy.+ let result = V.concat vecs'+ + return (Z :. V.length result, result)+
+ Data/Array/Repa/Operators/Traverse.hs view
@@ -0,0 +1,126 @@+{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE ExplicitForAll #-}++module Data.Array.Repa.Operators.Traverse+ ( traverse, unsafeTraverse+ , traverse2, unsafeTraverse2+ , traverse3, unsafeTraverse3+ , traverse4, unsafeTraverse4)+where+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Internals.Base+import Data.Array.Repa.Shape as S++-- Generic Traversal -----------------------------------------------------------------------------+-- | Unstructured traversal.+traverse+ :: forall sh sh' a b+ . (Shape sh, Shape sh', Elt a)+ => Array sh a -- ^ Source array.+ -> (sh -> sh') -- ^ Function to produce the extent of the result.+ -> ((sh -> a) -> sh' -> b) -- ^ Function to produce elements of the result. + -- It is passed a lookup function to get elements of the source.+ -> Array sh' b+ +{-# INLINE traverse #-}+traverse arr transExtent newElem+ = arr `deepSeqArray` + fromFunction (transExtent (extent arr)) (newElem (arr !))+++{-# INLINE unsafeTraverse #-}+unsafeTraverse arr transExtent newElem+ = arr `deepSeqArray` + fromFunction (transExtent (extent arr)) (newElem (unsafeIndex arr))+++-- | Unstructured traversal over two arrays at once.+traverse2, unsafeTraverse2+ :: forall sh sh' sh'' a b c+ . ( Shape sh, Shape sh', Shape sh''+ , Elt a, Elt b, Elt c)+ => Array sh a -- ^ First source array.+ -> Array sh' b -- ^ Second source array.+ -> (sh -> sh' -> sh'') -- ^ Function to produce the extent of the result.+ -> ((sh -> a) -> (sh' -> b) + -> (sh'' -> c)) -- ^ Function to produce elements of the result.+ -- It is passed lookup functions to get elements of the + -- source arrays.+ -> Array sh'' c ++{-# INLINE traverse2 #-}+traverse2 arrA arrB transExtent newElem+ = arrA `deepSeqArray` arrB `deepSeqArray`+ fromFunction+ (transExtent (extent arrA) (extent arrB)) + (newElem (arrA !) (arrB !))++{-# INLINE unsafeTraverse2 #-}+unsafeTraverse2 arrA arrB transExtent newElem+ = arrA `deepSeqArray` arrB `deepSeqArray`+ fromFunction+ (transExtent (extent arrA) (extent arrB)) + (newElem (unsafeIndex arrA) (unsafeIndex arrB))+++-- | Unstructured traversal over three arrays at once.+traverse3, unsafeTraverse3+ :: forall sh1 sh2 sh3 sh4+ a b c d + . ( Shape sh1, Shape sh2, Shape sh3, Shape sh4+ , Elt a, Elt b, Elt c, Elt d)+ => Array sh1 a + -> Array sh2 b + -> Array sh3 c + -> (sh1 -> sh2 -> sh3 -> sh4) + -> ( (sh1 -> a) -> (sh2 -> b) + -> (sh3 -> c)+ -> sh4 -> d ) + -> Array sh4 d++{-# INLINE traverse3 #-}+traverse3 arrA arrB arrC transExtent newElem+ = arrA `deepSeqArray` arrB `deepSeqArray` arrC `deepSeqArray`+ fromFunction+ (transExtent (extent arrA) (extent arrB) (extent arrC)) + (newElem (arrA !) (arrB !) (arrC !))++{-# INLINE unsafeTraverse3 #-}+unsafeTraverse3 arrA arrB arrC transExtent newElem+ = arrA `deepSeqArray` arrB `deepSeqArray` arrC `deepSeqArray`+ fromFunction+ (transExtent (extent arrA) (extent arrB) (extent arrC)) + (newElem (unsafeIndex arrA) (unsafeIndex arrB) (unsafeIndex arrC))+++-- | Unstructured traversal over four arrays at once.+traverse4, unsafeTraverse4+ :: forall sh1 sh2 sh3 sh4 sh5 + a b c d e+ . ( Shape sh1, Shape sh2, Shape sh3, Shape sh4, Shape sh5+ , Elt a, Elt b, Elt c, Elt d, Elt e)+ => Array sh1 a + -> Array sh2 b + -> Array sh3 c + -> Array sh4 d + -> (sh1 -> sh2 -> sh3 -> sh4 -> sh5 ) + -> ( (sh1 -> a) -> (sh2 -> b) + -> (sh3 -> c) -> (sh4 -> d)+ -> sh5 -> e ) + -> Array sh5 e ++{-# INLINE traverse4 #-}+traverse4 arrA arrB arrC arrD transExtent newElem+ = arrA `deepSeqArray` arrB `deepSeqArray` arrC `deepSeqArray` arrD `deepSeqArray` + fromFunction+ (transExtent (extent arrA) (extent arrB) (extent arrC) (extent arrD)) + (newElem (arrA !) (arrB !) (arrC !) (arrD !))+++{-# INLINE unsafeTraverse4 #-}+unsafeTraverse4 arrA arrB arrC arrD transExtent newElem+ = arrA `deepSeqArray` arrB `deepSeqArray` arrC `deepSeqArray` arrD `deepSeqArray` + fromFunction+ (transExtent (extent arrA) (extent arrB) (extent arrC) (extent arrD)) + (newElem (unsafeIndex arrA) (unsafeIndex arrB) (unsafeIndex arrC) (unsafeIndex arrD))+
+ Data/Array/Repa/Properties.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE ScopedTypeVariables #-}++module Data.Array.Repa.Properties+ ( props_DataArrayRepaIndex+ , props_DataArrayRepa)+where+import Data.Array.Repa as R+import qualified Data.Array.Repa.Shape as S+import Data.Array.Repa.Arbitrary+import Control.Monad+import Test.QuickCheck+import Prelude as P++stage = "Data.Array.Repa.Properties"+++-- Data.Array.Repa.Index --------------------------------------------------------------------------+-- | QuickCheck properties for "Data.Array.Repa.Index".+props_DataArrayRepaIndex :: [(String, Property)]+props_DataArrayRepaIndex+ = [(stage P.++ "." P.++ name, test) | (name, test)+ <- [ ("toIndexFromIndex/DIM1", property prop_toIndexFromIndex_DIM1) + , ("toIndexFromIndex/DIM2", property prop_toIndexFromIndex_DIM2) ]]++prop_toIndexFromIndex_DIM1 sh ix+ = (sizeIsValid sh)+ ==> (inShape sh ix)+ ==> fromIndex sh (toIndex sh ix) == ix+ where _types = ( sh :: DIM1+ , ix :: DIM1)++prop_toIndexFromIndex_DIM2+ = forAll arbitraryShape $ \(sh :: DIM2) ->+ forAll (genInShape2 sh) $ \(ix :: DIM2) ->+ fromIndex sh (toIndex sh ix) == ix++ + ++-- Data.Array.Repa --------------------------------------------------------------------------------+-- | QuickCheck properties for "Data.Array.Repa" and its children.+props_DataArrayRepa :: [(String, Property)]+props_DataArrayRepa+ = props_DataArrayRepaIndex+ P.++ [(stage P.++ "." P.++ name, test) | (name, test)+ <- [ ("id_force/DIM5", property prop_id_force_DIM5)+ , ("id_toScalarUnit", property prop_id_toScalarUnit)+ , ("id_toListFromList/DIM3", property prop_id_toListFromList_DIM3) + , ("id_transpose/DIM4", property prop_id_transpose_DIM4)+ , ("reshapeTransposeSize/DIM3", property prop_reshapeTranspose_DIM3)+ , ("appendIsAppend/DIM3", property prop_appendIsAppend_DIM3)+ , ("sumAllIsSum/DIM3", property prop_sumAllIsSum_DIM3) ]]+ ++-- The Eq instance uses fold and zipWith.+prop_id_force_DIM5+ = forAll (arbitrarySmallArray 10) $ \(arr :: Array DIM5 Int) ->+ arr == force arr+ +prop_id_toScalarUnit (x :: Int)+ = toScalar (singleton x) == x++-- Conversions ------------------------+prop_id_toListFromList_DIM3+ = forAll (arbitrarySmallShape 10) $ \(sh :: DIM3) ->+ forAll (arbitraryListOfLength (S.size sh)) $ \(xx :: [Int]) ->+ toList (fromList sh xx) == xx++-- Index Space Transforms -------------+prop_id_transpose_DIM4+ = forAll (arbitrarySmallArray 20) $ \(arr :: Array DIM3 Int) ->+ transpose (transpose arr) == arr++-- A reshaped array has the same size and sum as the original+prop_reshapeTranspose_DIM3+ = forAll (arbitrarySmallArray 20) $ \(arr :: Array DIM3 Int) ->+ let arr' = transpose arr+ sh' = extent arr'+ in (S.size $ extent arr) == S.size (extent (reshape sh' arr))+ && (sumAll arr == sumAll arr')++prop_appendIsAppend_DIM3+ = forAll (arbitrarySmallArray 20) $ \(arr1 :: Array DIM3 Int) ->+ sumAll (append arr1 arr1) == (2 * sumAll arr1)++-- Reductions --------------------------+prop_sumAllIsSum_DIM3+ = forAll (arbitrarySmallShape 100) $ \(sh :: DIM2) ->+ forAll (arbitraryListOfLength (S.size sh)) $ \(xx :: [Int]) -> + sumAll (fromList sh xx) == P.sum xx+++-- Utils ------------------------------------------------------------------------------------------+genInShape2 :: DIM2 -> Gen DIM2+genInShape2 (Z :. yMax :. xMax)+ = do y <- liftM (`mod` yMax) $ arbitrary+ x <- liftM (`mod` xMax) $ arbitrary+ return $ Z :. y :. x
− Data/Array/Repa/QuickCheck.hs
@@ -1,21 +0,0 @@---- Utils to help with testing. Not exported.-module Data.Array.Repa.QuickCheck- (arbitraryListOfLength)-where-import Test.QuickCheck- - -arbitraryListOfLength - :: Arbitrary a- => Int -> Gen [a]--arbitraryListOfLength n- | n == 0 = return []- | otherwise- = do i <- arbitrary- rest <- arbitraryListOfLength (n - 1)- return $ i : rest- - -
Data/Array/Repa/Shape.hs view
@@ -11,7 +11,7 @@ class Eq sh => Shape sh where -- | Get the number of dimensions in a shape.- dim :: sh -> Int + rank :: sh -> Int -- | The shape of an array of size zero, with a particular dimensionality. zeroDim :: sh@@ -22,6 +22,8 @@ -- | Compute the intersection of two shapes. intersectDim :: sh -> sh -> sh + -- | Add the coordinates of two shapes componentwise+ addDim :: sh -> sh -> sh -- | Get the total number of elements in an array with this shape. size :: sh -> Int @@ -44,7 +46,8 @@ -> sh -- | Check whether an index is within a given shape.- inRange :: sh -- ^ Start index for range.+ inShapeRange+ :: sh -- ^ Start index for range. -> sh -- ^ Final index for range. -> sh -- ^ Index to check for. -> Bool@@ -67,5 +70,6 @@ -> sh -- ^ Index. -> Bool +{-# INLINE inShape #-} inShape sh ix- = inRange zeroDim sh ix+ = inShapeRange zeroDim sh ix
Data/Array/Repa/Slice.hs view
@@ -47,31 +47,36 @@ instance Slice Z where+ {-# INLINE sliceOfFull #-} sliceOfFull _ _ = Z++ {-# INLINE fullOfSlice #-} fullOfSlice _ _ = Z instance Slice (Any sh) where+ {-# INLINE sliceOfFull #-} sliceOfFull _ sh = sh++ {-# INLINE fullOfSlice #-} fullOfSlice _ sh = sh instance Slice sl => Slice (sl :. Int) where+ {-# INLINE sliceOfFull #-} sliceOfFull (fsl :. _) (ssl :. _) = sliceOfFull fsl ssl + {-# INLINE fullOfSlice #-} fullOfSlice (fsl :. n) ssl = fullOfSlice fsl ssl :. n instance Slice sl => Slice (sl :. All) where + {-# INLINE sliceOfFull #-} sliceOfFull (fsl :. All) (ssl :. s) = sliceOfFull fsl ssl :. s + {-# INLINE fullOfSlice #-} fullOfSlice (fsl :. All) (ssl :. s) = fullOfSlice fsl ssl :. s- - -- -
+ Data/Array/Repa/Specialised/Dim2.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE BangPatterns #-}++-- | Functions specialised for arrays of dimension 2.+module Data.Array.Repa.Specialised.Dim2+ ( isInside2+ , isOutside2+ , clampToBorder2+ , makeBordered2)+where+import Data.Array.Repa+++-- | Check if an index lies inside the given extent.+-- As opposed to `inRange` from "Data.Array.Repa.Index", +-- this is a short-circuited test that checks that lowest dimension first.+isInside2 + :: DIM2 -- ^ Extent of array.+ -> DIM2 -- ^ Index to check.+ -> Bool ++{-# INLINE isInside2 #-}+isInside2 ex = not . isOutside2 ex+++-- | Check if an index lies outside the given extent.+-- As opposed to `inRange` from "Data.Array.Repa.Index",+-- this is a short-circuited test that checks the lowest dimension first.+isOutside2 + :: DIM2 -- ^ Extent of array. + -> DIM2 -- ^ Index to check.+ -> Bool+ +{-# INLINE isOutside2 #-}+isOutside2 (_ :. yLen :. xLen) (_ :. yy :. xx) + | xx < 0 = True+ | xx >= xLen = True+ | yy < 0 = True+ | yy >= yLen = True+ | otherwise = False+++-- | Given the extent of an array, clamp the components of an index so they+-- lie within the given array. Outlying indices are clamped to the index+-- of the nearest border element.+clampToBorder2 + :: DIM2 -- ^ Extent of array.+ -> DIM2 -- ^ Index to clamp.+ -> DIM2++{-# INLINE clampToBorder2 #-}+clampToBorder2 (_ :. yLen :. xLen) (sh :. j :. i)+ = clampX j i+ where {-# INLINE clampX #-}+ clampX !y !x+ | x < 0 = clampY y 0+ | x >= xLen = clampY y (xLen - 1)+ | otherwise = clampY y x+ + {-# INLINE clampY #-}+ clampY !y !x+ | y < 0 = sh :. 0 :. x+ | y >= yLen = sh :. (yLen - 1) :. x+ | otherwise = sh :. y :. x+++-- | Make a 2D partitioned array given two generators, one to produce elements in the +-- border region, and one to produce values in the internal region.+-- The border must be the same width on all sides.+makeBordered2+ :: Elt a+ => DIM2 -- ^ Extent of array.+ -> Int -- ^ Width of border.+ -> Generator DIM2 a -- ^ Generator for border elements.+ -> Generator DIM2 a -- ^ Generator for internal elements.+ -> Array DIM2 a++{-# INLINE makeBordered2 #-}+makeBordered2 sh@(_ :. aHeight :. aWidth) borderWidth genInternal genBorder+ = let+ -- minimum and maximum indicies of values in the inner part of the image.+ !xMin = borderWidth+ !yMin = borderWidth+ !xMax = aWidth - borderWidth - 1+ !yMax = aHeight - borderWidth - 1++ -- | Range of values where some of the data needed by the stencil is outside the image.+ rectsBorder+ = [ Rect (Z :. 0 :. 0) (Z :. yMin -1 :. aWidth - 1) -- bot + , Rect (Z :. yMax + 1 :. 0) (Z :. aHeight - 1 :. aWidth - 1) -- top+ , Rect (Z :. yMin :. 0) (Z :. yMax :. xMin - 1) -- left+ , Rect (Z :. yMin :. xMax + 1) (Z :. yMax :. aWidth - 1) ] -- right++ {-# INLINE inBorder #-}+ inBorder = not . inInternal++ -- Range of values where we don't need to worry about the border+ rectsInternal + = [ Rect (Z :. yMin :. xMin) (Z :. yMax :. xMax ) ]++ {-# INLINE inInternal #-}+ inInternal (Z :. y :. x)+ = x >= xMin && x <= xMax + && y >= yMin && y <= yMax++ in Array sh+ [ Region (RangeRects inBorder rectsBorder) genInternal+ , Region (RangeRects inInternal rectsInternal) genBorder ]++
+ Data/Array/Repa/Stencil.hs view
@@ -0,0 +1,271 @@+{-# LANGUAGE MagicHash, PatternGuards, BangPatterns, TemplateHaskell, QuasiQuotes, + ParallelListComp, TypeOperators, ExplicitForAll, ScopedTypeVariables #-}+{-# OPTIONS -Wnot #-}++-- | Efficient computation of stencil based convolutions.+--+-- This is specialised for stencils up to 7x7.+-- Due to limitations in the GHC optimiser, using larger stencils doesn't work, and will yield `error`+-- at runtime. We can probably increase the limit if required -- just ask.+--+-- The focus of the stencil is in the center of the 7x7 tile, which has coordinates (0, 0).+-- All coefficients in the stencil must fit in the tile, so they can be given X,Y coordinates up to+-- +/- 3 positions. The stencil can be any shape, and need not be symmetric -- provided it fits in the 7x7 tile.+--+module Data.Array.Repa.Stencil+ ( Stencil (..)+ , Boundary (..)++ -- * Stencil creation.+ , makeStencil, makeStencil2++ -- * Stencil operators.+ , mapStencil2, forStencil2+ , mapStencilFrom2, forStencilFrom2+ + -- From Data.Array.Repa.Stencil.Template+ , stencil2)+where+import Data.Array.Repa as R+import Data.Array.Repa.Internals.Base as R+import Data.Array.Repa.Stencil.Base+import Data.Array.Repa.Stencil.Template+import Data.Array.Repa.Specialised.Dim2+import qualified Data.Array.Repa.Shape as S+import qualified Data.Vector.Unboxed as V+import Data.List as List+import GHC.Exts+import GHC.Base+import Debug.Trace++-- | A index into the flat array.+-- Should be abstract outside the stencil modules.+data Cursor + = Cursor Int+++-- Wrappers ---------------------------------------------------------------------------------------+-- | Like `mapStencil2` but with the parameters flipped.+forStencil2+ :: Elt a+ => Boundary a+ -> Array DIM2 a+ -> Stencil DIM2 a+ -> Array DIM2 a++{-# INLINE forStencil2 #-}+forStencil2 boundary arr stencil+ = mapStencil2 boundary stencil arr+++-- | Like `mapStencilFrom2` but with the parameters flipped.+forStencilFrom2+ :: (Elt a, Elt b)+ => Boundary a+ -> Array DIM2 b+ -> (b -> a)+ -> Stencil DIM2 a+ -> Array DIM2 a++{-# INLINE forStencilFrom2 #-}+forStencilFrom2 boundary arr from stencil+ = mapStencilFrom2 boundary stencil arr from+++-- | Apply a stencil to every element of a 2D array.+-- The array must be manifest else `error`.+mapStencil2+ :: Elt a+ => Boundary a+ -> Stencil DIM2 a+ -> Array DIM2 a+ -> Array DIM2 a++{-# INLINE mapStencil2 #-}+mapStencil2 boundary stencil arr+ = mapStencilFrom2 boundary stencil arr id+++---------------------------------------------------------------------------------------------------+-- | Apply a stencil to every element of a 2D array.+-- The array must be manifest else `error`.+mapStencilFrom2 + :: (Elt a, Elt b)+ => Boundary a -- ^ How to handle the boundary of the array.+ -> Stencil DIM2 a -- ^ Stencil to apply.+ -> Array DIM2 b -- ^ Array to apply stencil to.+ -> (b -> a) -- ^ Apply this function to values read from the array before+ -- transforming them with the stencil.+ -> Array DIM2 a++{-# INLINE mapStencilFrom2 #-}+mapStencilFrom2 boundary stencil@(StencilStatic sExtent zero load) arr preConvert+ = let (_ :. aHeight :. aWidth) = extent arr+ (_ :. sHeight :. sWidth) = sExtent++ sHeight2 = sHeight `div` 2+ sWidth2 = sWidth `div` 2++ -- minimum and maximum indicies of values in the inner part of the image.+ !xMin = sWidth2+ !yMin = sHeight2+ !xMax = aWidth - sWidth2 - 1+ !yMax = aHeight - sHeight2 - 1++ -- Rectangles -----------------------+ -- range of values where we don't need to worry about the border+ rectsInternal + = [ Rect (Z :. yMin :. xMin) (Z :. yMax :. xMax ) ]++ {-# INLINE inInternal #-}+ inInternal (Z :. y :. x)+ = x >= xMin && x <= xMax + && y >= yMin && y <= yMax+ ++ -- range of values where some of the data needed by the stencil is outside the image.+ rectsBorder+ = [ Rect (Z :. 0 :. 0) (Z :. yMin -1 :. aWidth - 1) -- bot + , Rect (Z :. yMax + 1 :. 0) (Z :. aHeight - 1 :. aWidth - 1) -- top+ , Rect (Z :. yMin :. 0) (Z :. yMax :. xMin - 1) -- left+ , Rect (Z :. yMin :. xMax + 1) (Z :. yMax :. aWidth - 1) ] -- right++ {-# INLINE inBorder #-}+ inBorder = not . inInternal+++ -- Cursor functions ----------------+ {-# INLINE makeCursor' #-}+ makeCursor' (Z :. y :. x) + = Cursor (x + y * aWidth)+ + {-# INLINE shiftCursor' #-}+ shiftCursor' ix (Cursor off)+ = Cursor+ $ case ix of+ Z :. y :. x -> off + y * aWidth + x+ + {-# INLINE getInner' #-}+ getInner' cur + = unsafeAppStencilCursor2 shiftCursor' stencil+ arr preConvert cur+ + {-# INLINE getBorder' #-}+ getBorder' cur+ = case boundary of+ BoundConst c -> c+ BoundClamp -> unsafeAppStencilCursor2_clamp addDim stencil+ arr preConvert cur+ + in Array (extent arr)+ [ Region (RangeRects inBorder rectsBorder)+ (GenCursor id addDim getBorder')+ + , Region (RangeRects inInternal rectsInternal)+ (GenCursor makeCursor' shiftCursor' getInner') ]+++unsafeAppStencilCursor2+ :: (Elt a, Elt b)+ => (DIM2 -> Cursor -> Cursor)+ -> Stencil DIM2 a+ -> Array DIM2 b+ -> (b -> a)+ -> Cursor + -> a++{-# INLINE [1] unsafeAppStencilCursor2 #-}+unsafeAppStencilCursor2 shift+ stencil@(StencilStatic sExtent zero load)+ arr@(Array aExtent [Region RangeAll (GenManifest vec)]) preConvert+ cur@(Cursor off)++ | _ :. sHeight :. sWidth <- sExtent+ , _ :. aHeight :. aWidth <- aExtent+ , sHeight <= 7, sWidth <= 7+ = let + -- Get data from the manifest array.+ {-# INLINE [0] getData #-}+ getData (Cursor cur) = preConvert $ vec `V.unsafeIndex` cur+ + -- Build a function to pass data from the array to our stencil.+ {-# INLINE oload #-}+ oload oy ox + = let !cur' = shift (Z :. oy :. ox) cur+ in load (Z :. oy :. ox) (getData cur')+ + in template7x7 oload zero+++-- | Like above, but clamp out of bounds array values to the closest real value.+unsafeAppStencilCursor2_clamp+ :: forall a b. (Elt a, Elt b)+ => (DIM2 -> DIM2 -> DIM2)+ -> Stencil DIM2 a+ -> Array DIM2 b + -> (b -> a)+ -> DIM2+ -> a++{-# INLINE [1] unsafeAppStencilCursor2_clamp #-}+unsafeAppStencilCursor2_clamp shift+ stencil@(StencilStatic sExtent zero load)+ arr@(Array aExtent [Region RangeAll (GenManifest vec)]) preConvert+ cur++ | _ :. sHeight :. sWidth <- sExtent+ , _ :. aHeight :. aWidth <- aExtent+ , sHeight <= 7, sWidth <= 7+ = let + -- Get data from the manifest array.+ {-# INLINE [0] getData #-}+ getData :: DIM2 -> a+ getData (Z :. y :. x)+ = wrapLoadX x y++ -- TODO: Inlining this into above makes SpecConstr choke+ wrapLoadX :: Int -> Int -> a+ wrapLoadX !x !y+ | x < 0 = wrapLoadY 0 y+ | x >= aWidth = wrapLoadY (aWidth - 1) y+ | otherwise = wrapLoadY x y+ + {-# INLINE wrapLoadY #-}+ wrapLoadY :: Int -> Int -> a+ wrapLoadY !x !y+ | y < 0 = loadXY x 0+ | y >= aHeight = loadXY x (aHeight - 1)+ | otherwise = loadXY x y+ + {-# INLINE loadXY #-}+ loadXY :: Int -> Int -> a+ loadXY !x !y+ = preConvert $ vec `V.unsafeIndex` (x + y * aWidth)+ + -- Build a function to pass data from the array to our stencil.+ {-# INLINE oload #-}+ oload oy ox + = let !cur' = shift (Z :. oy :. ox) cur+ in load (Z :. oy :. ox) (getData cur')+ + in template7x7 oload zero++++-- | Data template for stencils up to 7x7.+template7x7+ :: (Int -> Int -> a -> a)+ -> a -> a++{-# INLINE [1] template7x7 #-}+template7x7 f zero+ = f (-3) (-3) $ f (-3) (-2) $ f (-3) (-1) $ f (-3) 0 $ f (-3) 1 $ f (-3) 2 $ f (-3) 3+ $ f (-2) (-3) $ f (-2) (-2) $ f (-2) (-1) $ f (-2) 0 $ f (-2) 1 $ f (-2) 2 $ f (-2) 3+ $ f (-1) (-3) $ f (-1) (-2) $ f (-1) (-1) $ f (-1) 0 $ f (-1) 1 $ f (-1) 2 $ f (-1) 3+ $ f 0 (-3) $ f 0 (-2) $ f 0 (-1) $ f 0 0 $ f 0 1 $ f 0 2 $ f 0 3+ $ f 1 (-3) $ f 1 (-2) $ f 1 (-1) $ f 1 0 $ f 1 1 $ f 1 2 $ f 1 3+ $ f 2 (-3) $ f 2 (-2) $ f 2 (-1) $ f 2 0 $ f 2 1 $ f 2 2 $ f 2 3+ $ f 3 (-3) $ f 3 (-2) $ f 3 (-1) $ f 3 0 $ f 3 1 $ f 3 2 $ f 3 3+ $ zero++
+ Data/Array/Repa/Stencil/Base.hs view
@@ -0,0 +1,59 @@++-- | Basic definitions for stencil handling.+module Data.Array.Repa.Stencil.Base+ ( Boundary (..)+ , Stencil (..)+ , makeStencil, makeStencil2)+where+import Data.Array.Repa.Internals.Elt+import Data.Array.Repa.Index++-- | How to handle the case when the stencil lies partly outside the array.+data Boundary a+ -- | Treat points outside as having a constant value.+ = BoundConst a ++ -- | Clamp points outside to the same value as the edge pixel.+ | BoundClamp+ deriving (Show)+++-- | Represents a convolution stencil that we can apply to array. Only statically known stencils+-- are supported right now.+data Stencil sh a++ -- | Static stencils are used when the coefficients are fixed,+ -- and known at compile time.+ = StencilStatic+ { stencilExtent :: !sh+ , stencilZero :: !a+ , stencilAcc :: !(sh -> a -> a -> a) }+ + +-- | Make a stencil from a function yielding coefficients at each index.+makeStencil+ :: (Elt a, Num a) + => sh -- ^ Extent of stencil.+ -> (sh -> Maybe a) -- ^ Get the coefficient at this index.+ -> Stencil sh a++{-# INLINE makeStencil #-}+makeStencil ex getCoeff+ = StencilStatic ex 0 + $ \ix val acc+ -> case getCoeff ix of+ Nothing -> acc+ Just coeff -> acc + val * coeff+++-- | Wrapper for `makeStencil` that requires a DIM2 stencil.+makeStencil2+ :: (Elt a, Num a)+ => Int -> Int -- ^ extent of stencil+ -> (DIM2 -> Maybe a) -- ^ Get the coefficient at this index.+ -> Stencil DIM2 a++{-# INLINE makeStencil2 #-}+makeStencil2 height width getCoeff+ = makeStencil (Z :. height :. width) getCoeff+
+ Data/Array/Repa/Stencil/Template.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes, ParallelListComp #-}++-- | Template +module Data.Array.Repa.Stencil.Template+ (stencil2)+where+import Data.Array.Repa.Index+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import qualified Data.List as List++-- | QuasiQuoter for producing a static stencil defintion.+-- +-- A definition like +-- +-- @+-- [stencil2| 0 1 0+-- 1 0 1+-- 0 1 0 |]+-- @+--+-- Is converted to:+-- +-- @+-- makeStencil2 (Z:.3:.3)+-- (\\ix -> case ix of+-- Z :. -1 :. 0 -> Just 1+-- Z :. 0 :. -1 -> Just 1+-- Z :. 0 :. 1 -> Just 1+-- Z :. 1 :. 0 -> Just 1+-- _ -> Nothing)+-- @+--+stencil2 :: QuasiQuoter+stencil2 = QuasiQuoter + { quoteExp = parseStencil2+ , quotePat = undefined+ , quoteType = undefined+ , quoteDec = undefined }+++-- | Parse a stencil definition.+-- TODO: make this more robust.+parseStencil2 :: String -> Q Exp+parseStencil2 str + = let + -- Determine the extent of the stencil based on the layout.+ -- TODO: make this more robust. In particular, handle blank+ -- lines at the start of the definition.+ line1 : _ = lines str+ sizeX = fromIntegral $ length $ lines str+ sizeY = fromIntegral $ length $ words line1+ + -- TODO: this probably doesn't work for stencils who's extents are even.+ minX = negate (sizeX `div` 2)+ minY = negate (sizeY `div` 2)+ maxX = sizeX `div` 2+ maxY = sizeY `div` 2++ -- List of coefficients for the stencil.+ coeffs = (List.map read $ words str) :: [Integer]+ + in makeStencil2' sizeX sizeY+ $ filter (\(_, _, v) -> v /= 0)+ $ [ (fromIntegral y, fromIntegral x, fromIntegral v)+ | y <- [minX, minX + 1 .. maxX]+ , x <- [minY, minY + 1 .. maxY]+ | v <- coeffs ]+++makeStencil2'+ :: Integer -> Integer+ -> [(Integer, Integer, Integer)]+ -> Q Exp++makeStencil2' sizeX sizeY coeffs+ = do let makeStencil' = mkName "makeStencil2"+ let dot' = mkName ":."+ let just' = mkName "Just"+ ix' <- newName "ix"+ z' <- [p| Z |]+ coeffs' <- newName "coeffs"+ + let fnCoeffs + = LamE [VarP ix']+ $ CaseE (VarE ix') + $ [ Match (InfixP (InfixP z' dot' (LitP (IntegerL oy))) dot' (LitP (IntegerL ox)))+ (NormalB $ ConE just' `AppE` LitE (IntegerL v))+ [] | (oy, ox, v) <- coeffs ]+ ++ [Match WildP + (NormalB $ ConE (mkName "Nothing")) []]+ + return + $ AppE (VarE makeStencil' `AppE` (LitE (IntegerL sizeX)) `AppE` (LitE (IntegerL sizeY)))+ $ LetE [ PragmaD (InlineP coeffs' (InlineSpec True False Nothing))+ , ValD (VarP coeffs') (NormalB fnCoeffs) [] ]+ (VarE coeffs')+ +
repa.cabal view
@@ -1,5 +1,5 @@ Name: repa-Version: 1.1.0.0+Version: 2.0.0.1 License: BSD3 License-file: LICENSE Author: The DPH Team@@ -11,7 +11,6 @@ Homepage: http://trac.haskell.org/repa Bug-reports: http://trac.haskell.org/repa/newticket Description:- NOTE: You must use the GHC head branch > 6.13.20100309 to get decent performance. Repa provides high performance, regular, multi-dimensional, shape polymorphic parallel arrays. All numeric data is stored unboxed. Functions written with the Repa combinators are automatically parallel provided you supply +RTS -Nwhatever on the command@@ -20,24 +19,46 @@ Synopsis: High performance, regular, shape polymorphic parallel arrays. -Tested-with: GHC == 6.13.20100309, GHC == 6.12.1+Tested-with: GHC == 7.0.1 Library Build-Depends: base == 4.*,- dph-prim-par == 0.4.*,- dph-prim-seq == 0.4.*,- QuickCheck == 2.1.*+ ghc-prim == 0.2.*,+ vector >= 0.7 && < 0.8,+ QuickCheck >= 2.3 && < 2.5,+ template-haskell >= 2.5 && < 2.6 ghc-options:- -Odph -Wall -fno-warn-missing-signatures+ -Wall -fno-warn-missing-signatures+ -Odph+ -funbox-strict-fields+ -fcpr-off Exposed-modules: Data.Array.Repa Data.Array.Repa.Index Data.Array.Repa.Shape Data.Array.Repa.Slice+ Data.Array.Repa.Stencil+ Data.Array.Repa.Arbitrary+ Data.Array.Repa.Properties+ Data.Array.Repa.Specialised.Dim2 Other-modules:- Data.Array.Repa.QuickCheck- + Data.Array.Repa.Operators.IndexSpace+ Data.Array.Repa.Operators.Traverse+ Data.Array.Repa.Operators.Interleave+ Data.Array.Repa.Operators.Mapping+ Data.Array.Repa.Operators.Reduction+ Data.Array.Repa.Operators.Select+ Data.Array.Repa.Internals.Elt+ Data.Array.Repa.Internals.Base+ Data.Array.Repa.Internals.Gang+ Data.Array.Repa.Internals.EvalChunked+ Data.Array.Repa.Internals.EvalBlockwise+ Data.Array.Repa.Internals.EvalCursored+ Data.Array.Repa.Internals.Forcing+ Data.Array.Repa.Internals.Select+ Data.Array.Repa.Stencil.Base+ Data.Array.Repa.Stencil.Template