accelerate-0.8.0.0: Data/Array/Accelerate/Language.hs
{-# LANGUAGE FlexibleContexts, TypeFamilies, RankNTypes, ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-missing-methods #-}
-- Module : Data.Array.Accelerate.Language
-- Copyright : [2009..2010] Manuel M T Chakravarty, Gabriele Keller, Sean Lee
-- License : BSD3
--
-- Maintainer : Manuel M T Chakravarty <chak@cse.unsw.edu.au>
-- Stability : experimental
-- Portability : non-portable (GHC extensions)
--
-- We use the dictionary view of overloaded operations (such as arithmetic and
-- bit manipulation) to reify such expressions. With non-overloaded
-- operations (such as, the logical connectives) and partially overloaded
-- operations (such as comparisons), we use the standard operator names with a
-- '*' attached. We keep the standard alphanumeric names as they can be
-- easily qualified.
module Data.Array.Accelerate.Language (
-- ** Array and scalar expressions
Acc, Exp, -- re-exporting from 'Smart'
-- ** Stencil specification
Boundary(..), Stencil, -- re-exporting from 'Smart'
-- ** Common stencil types
Stencil3, Stencil5, Stencil7, Stencil9,
Stencil3x3, Stencil5x3, Stencil3x5, Stencil5x5,
Stencil3x3x3, Stencil5x3x3, Stencil3x5x3, Stencil3x3x5, Stencil5x5x3, Stencil5x3x5,
Stencil3x5x5, Stencil5x5x5,
-- ** Scalar introduction
constant, -- re-exporting from 'Smart'
-- ** Array introduction
use, unit,
-- ** Shape manipulation
reshape,
-- ** Collective array operations
slice, replicate, zip, unzip, map, zipWith, scanl, scanr, fold, foldSeg,
permute, backpermute, stencil, stencil2,
-- ** Tuple construction and destruction
Tuple(..), fst, snd, curry, uncurry,
-- ** Conditional expressions
(?),
-- ** Array operations with a scalar result
(!), shape,
-- ** Methods of H98 classes that we need to redefine as their signatures change
(==*), (/=*), (<*), (<=*), (>*), (>=*), max, min,
bit, setBit, clearBit, complementBit, testBit,
shift, shiftL, shiftR,
rotate, rotateL, rotateR,
-- ** Standard functions that we need to redefine as their signatures change
(&&*), (||*), not,
-- ** Conversions
boolToInt, intToFloat, roundFloatToInt, truncateFloatToInt,
-- ** Constants
ignore
-- ** Instances of Bounded, Enum, Eq, Ord, Bits, Num, Real, Floating,
-- Fractional, RealFrac, RealFloat
) where
-- avoid clashes with Prelude functions
import Prelude hiding (replicate, zip, unzip, map, scanl, scanr, zipWith,
filter, max, min, not, const, fst, snd, curry, uncurry)
-- standard libraries
import Data.Bits (Bits((.&.), (.|.), xor, complement))
-- friends
import Data.Array.Accelerate.Type
import Data.Array.Accelerate.Array.Sugar hiding ((!), ignore, shape)
import qualified Data.Array.Accelerate.Array.Sugar as Sugar
import Data.Array.Accelerate.Smart
-- Collective operations
-- ---------------------
-- |Array inlet: makes an array available for processing using the Accelerate
-- language; triggers asynchronous host->device transfer if necessary.
--
use :: (Ix dim, Elem e) => Array dim e -> Acc (Array dim e)
use = Use
-- |Scalar inlet: injects a scalar (or a tuple of scalars) into a singleton
-- array for use in the Accelerate language.
--
unit :: Elem e => Exp e -> Acc (Scalar e)
unit = Unit
-- |Change the shape of an array without altering its contents, where
--
-- > precondition: size dim == size dim'
--
reshape :: (Ix dim, Ix dim', Elem e)
=> Exp dim
-> Acc (Array dim' e)
-> Acc (Array dim e)
reshape = Reshape
-- |Replicate an array across one or more dimensions as specified by the
-- *generalised* array index provided as the first argument.
--
-- For example, assuming 'arr' is a vector (one-dimensional array),
--
-- > replicate (2, All, 3) arr
--
-- yields a three dimensional array, where 'arr' is replicated twice across the
-- first and three times across the third dimension.
--
replicate :: (SliceIx slix, Elem e)
=> Exp slix
-> Acc (Array (Slice slix) e)
-> Acc (Array (SliceDim slix) e)
replicate = Replicate
-- |Index an array with a *generalised* array index (supplied as the second
-- argument). The result is a new array (possibly a singleton) containing
-- all dimensions in their entirety.
--
slice :: (SliceIx slix, Elem e)
=> Acc (Array (SliceDim slix) e)
-> Exp slix
-> Acc (Array (Slice slix) e)
slice = Index
-- |Combine the elements of two arrays pairwise. The shape of the result is
-- the intersection of the two argument shapes.
--
zip :: (Ix dim, Elem a, Elem b)
=> Acc (Array dim a)
-> Acc (Array dim b)
-> Acc (Array dim (a, b))
zip = zipWith (\x y -> tuple (x, y))
-- |The converse of 'zip', but the shape of the two results is identical to the
-- shape of the argument.
--
unzip :: (Ix dim, Elem a, Elem b)
=> Acc (Array dim (a, b))
-> (Acc (Array dim a), Acc (Array dim b))
unzip arr = (map fst arr, map snd arr)
-- |Apply the given function elementwise to the given array.
--
map :: (Ix dim, Elem a, Elem b)
=> (Exp a -> Exp b)
-> Acc (Array dim a)
-> Acc (Array dim b)
map = Map
-- |Apply the given binary function elementwise to the two arrays. The extent of the resulting
-- array is the intersection of the extents of the two source arrays.
--
zipWith :: (Ix dim, Elem a, Elem b, Elem c)
=> (Exp a -> Exp b -> Exp c)
-> Acc (Array dim a)
-> Acc (Array dim b)
-> Acc (Array dim c)
zipWith = ZipWith
-- |Prescan of a vector. The type 'a' together with the binary function
-- (first argument) and value (second argument) must form a monoid; i.e., the
-- function must be /associative/ and the value must be its /neutral element/.
--
-- The resulting vector of prescan values has the same size as the argument
-- vector. The resulting scalar is the reduction value.
--
scanl :: Elem a
=> (Exp a -> Exp a -> Exp a)
-> Exp a
-> Acc (Vector a)
-> (Acc (Vector a), Acc (Scalar a))
scanl f e arr = unpair (Scanl f e arr)
-- |The right-to-left dual of 'scanl'.
--
scanr :: Elem a
=> (Exp a -> Exp a -> Exp a)
-> Exp a
-> Acc (Vector a)
-> (Acc (Vector a), Acc (Scalar a))
scanr f e arr = unpair (Scanr f e arr)
-- |Reduction of an array. The type 'a' together with the binary function
-- (first argument) and value (second argument) must form a monoid; i.e., the
-- function must be /associative/ and the value must be its /neutral element/.
--
fold :: (Ix dim, Elem a)
=> (Exp a -> Exp a -> Exp a)
-> Exp a
-> Acc (Array dim a)
-> Acc (Scalar a)
fold = Fold
-- |Segmented reduction.
--
foldSeg :: Elem a
=> (Exp a -> Exp a -> Exp a)
-> Exp a
-> Acc (Vector a)
-> Acc Segments
-> Acc (Vector a)
foldSeg = FoldSeg
-- |Forward permutation specified by an index mapping. The result array is
-- initialised with the given defaults and any further values that are permuted
-- into the result array are added to the current value using the given
-- combination function.
--
-- The combination function must be /associative/. Elements that are mapped to
-- the magic value 'ignore' by the permutation function are being dropped.
--
permute :: (Ix dim, Ix dim', Elem a)
=> (Exp a -> Exp a -> Exp a) -- ^combination function
-> Acc (Array dim' a) -- ^array of default values
-> (Exp dim -> Exp dim') -- ^permutation
-> Acc (Array dim a) -- ^permuted array
-> Acc (Array dim' a)
permute = Permute
-- |Backward permutation
--
backpermute :: (Ix dim, Ix dim', Elem a)
=> Exp dim' -- ^shape of the result array
-> (Exp dim' -> Exp dim) -- ^permutation
-> Acc (Array dim a) -- ^permuted array
-> Acc (Array dim' a)
backpermute = Backpermute
-- Common stencil types
--
-- DIM1 stencil type
type Stencil3 a = (Exp a, Exp a, Exp a)
type Stencil5 a = (Exp a, Exp a, Exp a, Exp a, Exp a)
type Stencil7 a = (Exp a, Exp a, Exp a, Exp a, Exp a, Exp a, Exp a)
type Stencil9 a = (Exp a, Exp a, Exp a, Exp a, Exp a, Exp a, Exp a, Exp a, Exp a)
-- DIM2 stencil type
type Stencil3x3 a = (Stencil3 a, Stencil3 a, Stencil3 a)
type Stencil5x3 a = (Stencil5 a, Stencil5 a, Stencil5 a)
type Stencil3x5 a = (Stencil3 a, Stencil3 a, Stencil3 a, Stencil3 a, Stencil3 a)
type Stencil5x5 a = (Stencil5 a, Stencil5 a, Stencil5 a, Stencil5 a, Stencil5 a)
-- DIM3 stencil type
type Stencil3x3x3 a = (Stencil3x3 a, Stencil3x3 a, Stencil3x3 a)
type Stencil5x3x3 a = (Stencil5x3 a, Stencil5x3 a, Stencil5x3 a)
type Stencil3x5x3 a = (Stencil3x5 a, Stencil3x5 a, Stencil3x5 a)
type Stencil3x3x5 a = (Stencil3x3 a, Stencil3x3 a, Stencil3x3 a, Stencil3x3 a, Stencil3x3 a)
type Stencil5x5x3 a = (Stencil5x5 a, Stencil5x5 a, Stencil5x5 a)
type Stencil5x3x5 a = (Stencil5x3 a, Stencil5x3 a, Stencil5x3 a, Stencil5x3 a, Stencil5x3 a)
type Stencil3x5x5 a = (Stencil3x5 a, Stencil3x5 a, Stencil3x5 a, Stencil3x5 a, Stencil3x5 a)
type Stencil5x5x5 a = (Stencil5x5 a, Stencil5x5 a, Stencil5x5 a, Stencil5x5 a, Stencil5x5 a)
-- |Map a stencil over an array. In contrast to 'map', the domain of a stencil function is an
-- entire /neighbourhood/ of each array element. Neighbourhoods are sub-arrays centred around a
-- focal point. They are not necessarily rectangular, but they are symmetric in each dimension
-- and have an extent of at least three in each dimensions — due to the symmetry requirement, the
-- extent is necessarily odd. The focal point is the array position that is determined by the
-- stencil.
--
-- For those array positions where the neighbourhood extends past the boundaries of the source
-- array, a boundary condition determines the contents of the out-of-bounds neighbourhood
-- positions.
--
stencil :: (Ix dim, Elem a, Elem b, Stencil dim a stencil)
=> (stencil -> Exp b) -- ^stencil function
-> Boundary a -- ^boundary condition
-> Acc (Array dim a) -- ^source array
-> Acc (Array dim b) -- ^destination array
stencil = Stencil
-- |Map a binary stencil of an array. The extent of the resulting array is the intersection of
-- the extents of the two source arrays.
--
stencil2 :: (Ix dim, Elem a, Elem b, Elem c,
Stencil dim a stencil1,
Stencil dim b stencil2)
=> (stencil1 -> stencil2 -> Exp c) -- ^binary stencil function
-> Boundary a -- ^boundary condition #1
-> Acc (Array dim a) -- ^source array #1
-> Boundary b -- ^boundary condition #2
-> Acc (Array dim b) -- ^source array #2
-> Acc (Array dim c) -- ^destination array
stencil2 = Stencil2
-- Tuples
-- ------
class Tuple tup where
type TupleT tup
-- |Turn a tuple of scalar expressions into a scalar expressions that yields
-- a tuple.
--
tuple :: tup -> TupleT tup
-- |Turn a scalar expression that yields a tuple into a tuple of scalar
-- expressions.
untuple :: TupleT tup -> tup
instance (Elem a, Elem b) => Tuple (Exp a, Exp b) where
type TupleT (Exp a, Exp b) = Exp (a, b)
tuple = tup2
untuple = untup2
instance (Elem a, Elem b, Elem c) => Tuple (Exp a, Exp b, Exp c) where
type TupleT (Exp a, Exp b, Exp c) = Exp (a, b, c)
tuple = tup3
untuple = untup3
instance (Elem a, Elem b, Elem c, Elem d)
=> Tuple (Exp a, Exp b, Exp c, Exp d) where
type TupleT (Exp a, Exp b, Exp c, Exp d) = Exp (a, b, c, d)
tuple = tup4
untuple = untup4
instance (Elem a, Elem b, Elem c, Elem d, Elem e)
=> Tuple (Exp a, Exp b, Exp c, Exp d, Exp e) where
type TupleT (Exp a, Exp b, Exp c, Exp d, Exp e) = Exp (a, b, c, d, e)
tuple = tup5
untuple = untup5
-- |Extract the first component of a pair
--
fst :: forall a b. (Elem a, Elem b) => Exp (a, b) -> Exp a
fst e = let (x, _:: Exp b) = untuple e in x
-- |Extract the second component of a pair
snd :: forall a b. (Elem a, Elem b) => Exp (a, b) -> Exp b
snd e = let (_ :: Exp a, y) = untuple e in y
-- |Converts an uncurried function to a curried function
--
curry :: (Elem a, Elem b) => (Exp (a,b) -> Exp c) -> Exp a -> Exp b -> Exp c
curry f x y = f (tuple (x,y))
-- |Converts a curried function to a function on pairs
--
uncurry :: (Elem a, Elem b) => (Exp a -> Exp b -> Exp c) -> Exp (a,b) -> Exp c
uncurry f t = let (x,y) = untuple t in f x y
-- Conditional expressions
-- -----------------------
-- |Conditional expression.
--
infix 0 ?
(?) :: Elem t => Exp Bool -> (Exp t, Exp t) -> Exp t
c ? (t, e) = Cond c t e
-- Array operations with a scalar result
-- -------------------------------------
-- |Expression form that extracts a scalar from an array.
--
infixl 9 !
(!) :: (Ix dim, Elem e) => Acc (Array dim e) -> Exp dim -> Exp e
(!) = IndexScalar
shape :: (Ix dim, Elem dim) => Acc (Array dim e) -> Exp dim
shape = Shape
-- Instances of all relevant H98 classes
-- -------------------------------------
instance (Elem t, IsBounded t) => Bounded (Exp t) where
minBound = mkMinBound
maxBound = mkMaxBound
instance (Elem t, IsScalar t) => Enum (Exp t)
-- succ = mkSucc
-- pred = mkPred
-- FIXME: ops
instance (Elem t, IsScalar t) => Prelude.Eq (Exp t) where
-- FIXME: instance makes no sense with standard signatures
(==) = error "Prelude.Eq.== applied to EDSL types"
instance (Elem t, IsScalar t) => Prelude.Ord (Exp t) where
-- FIXME: instance makes no sense with standard signatures
compare = error "Prelude.Ord.compare applied to EDSL types"
instance (Elem t, IsNum t, IsIntegral t) => Bits (Exp t) where
(.&.) = mkBAnd
(.|.) = mkBOr
xor = mkBXor
complement = mkBNot
-- FIXME: argh, the rest have fixed types in their signatures
shift, shiftL, shiftR :: (Elem t, IsIntegral t) => Exp t -> Exp Int -> Exp t
shift x i = i ==* 0 ? (x, i <* 0 ? (x `shiftR` (-i), x `shiftL` i))
shiftL = mkBShiftL
shiftR = mkBShiftR
rotate, rotateL, rotateR :: (Elem t, IsIntegral t) => Exp t -> Exp Int -> Exp t
rotate x i = i ==* 0 ? (x, i <* 0 ? (x `rotateR` (-i), x `rotateL` i))
rotateL = mkBRotateL
rotateR = mkBRotateR
bit :: (Elem t, IsIntegral t) => Exp Int -> Exp t
bit x = 1 `shiftL` x
setBit, clearBit, complementBit :: (Elem t, IsIntegral t) => Exp t -> Exp Int -> Exp t
x `setBit` i = x .|. bit i
x `clearBit` i = x .&. complement (bit i)
x `complementBit` i = x `xor` bit i
testBit :: (Elem t, IsIntegral t) => Exp t -> Exp Int -> Exp Bool
x `testBit` i = (x .&. bit i) /=* 0
instance (Elem t, IsNum t) => Num (Exp t) where
(+) = mkAdd
(-) = mkSub
(*) = mkMul
negate = mkNeg
abs = mkAbs
signum = mkSig
fromInteger = constant . fromInteger
instance (Elem t, IsNum t) => Real (Exp t)
-- FIXME: Why did we include this class? We won't need `toRational' until
-- we support rational numbers in AP computations.
instance (Elem t, IsIntegral t) => Integral (Exp t) where
quot = mkQuot
rem = mkRem
div = mkIDiv
mod = mkMod
-- quotRem =
-- divMod =
-- toInteger = -- makes no sense
instance (Elem t, IsFloating t) => Floating (Exp t) where
pi = mkPi
sin = mkSin
cos = mkCos
tan = mkTan
asin = mkAsin
acos = mkAcos
atan = mkAtan
asinh = mkAsinh
acosh = mkAcosh
atanh = mkAtanh
exp = mkExpFloating
sqrt = mkSqrt
log = mkLog
(**) = mkFPow
logBase = mkLogBase
-- FIXME: add other ops
instance (Elem t, IsFloating t) => Fractional (Exp t) where
(/) = mkFDiv
recip = mkRecip
fromRational = constant . fromRational
-- FIXME: add other ops
instance (Elem t, IsFloating t) => RealFrac (Exp t)
-- FIXME: add ops
instance (Elem t, IsFloating t) => RealFloat (Exp t) where
atan2 = mkAtan2
-- FIXME: add ops
-- Methods from H98 classes, where we need other signatures
-- --------------------------------------------------------
infix 4 ==*, /=*, <*, <=*, >*, >=*
-- |Equality lifted into Accelerate expressions.
--
(==*) :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp Bool
(==*) = mkEq
-- |Inequality lifted into Accelerate expressions.
--
(/=*) :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp Bool
(/=*) = mkNEq
-- compare :: a -> a -> Ordering -- we have no enumerations at the moment
-- compare = ...
-- |Smaller-than lifted into Accelerate expressions.
--
(<*) :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp Bool
(<*) = mkLt
-- |Greater-or-equal lifted into Accelerate expressions.
--
(>=*) :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp Bool
(>=*) = mkGtEq
-- |Greater-than lifted into Accelerate expressions.
--
(>*) :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp Bool
(>*) = mkGt
-- |Smaller-or-equal lifted into Accelerate expressions.
--
(<=*) :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp Bool
(<=*) = mkLtEq
-- |Determine the maximum of two scalars.
--
max :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp t
max = mkMax
-- |Determine the minimum of two scalars.
--
min :: (Elem t, IsScalar t) => Exp t -> Exp t -> Exp t
min = mkMin
-- Non-overloaded standard functions, where we need other signatures
-- -----------------------------------------------------------------
-- |Conjunction
--
infixr 3 &&*
(&&*) :: Exp Bool -> Exp Bool -> Exp Bool
(&&*) = mkLAnd
-- |Disjunction
--
infixr 2 ||*
(||*) :: Exp Bool -> Exp Bool -> Exp Bool
(||*) = mkLOr
-- |Negation
--
not :: Exp Bool -> Exp Bool
not = mkLNot
-- Conversions
-- -----------
-- |Convert a Boolean value to an 'Int', where 'False' turns into '0' and 'True'
-- into '1'.
--
boolToInt :: Exp Bool -> Exp Int
boolToInt = mkBoolToInt
-- |Convert an Int to a Float
intToFloat :: Exp Int -> Exp Float
intToFloat = mkIntFloat
-- |Round Float to Int
roundFloatToInt :: Exp Float -> Exp Int
roundFloatToInt = mkRoundFloatInt
-- |Truncate Float to Int
truncateFloatToInt :: Exp Float -> Exp Int
truncateFloatToInt = mkTruncFloatInt
-- Constants
-- ---------
-- |Magic value identifying elements that are ignored in a forward permutation
--
ignore :: Ix dim => Exp dim
ignore = constant Sugar.ignore