dph-lifted-vseg-0.6.0.1: Data/Array/Parallel/PArray.hs
{-# OPTIONS -fno-spec-constr #-}
{-# LANGUAGE CPP, UndecidableInstances #-}
#include "fusion-phases.h"
-- | Unvectorised parallel arrays.
--
-- * These operators may be used directly by unvectorised client programs.
--
-- * They are also used by the "Data.Array.Parallel.Lifted.Combinators"
-- module to define the closure converted versions that vectorised code
-- uses.
--
-- * In general, the operators here are all unsafe and don't do bounds checks.
-- The lifted versions also don't check that each of the argument arrays
-- have the same length.
-- TODO:
-- Export unsafe versions from Data.Array.Parallel.PArray.Unsafe, and ensure
-- this module exports safe wrappers. We want to use the unsafe versions in
-- D.A.P.Lifted.Combinators for performance reasons, but the user facing PArray
-- functions should all be safe. In particular, the vectoriser guarantees
-- that all arrays passed to lifted functions will have the same length, but
-- the user may not obey this restriction.
--
module Data.Array.Parallel.PArray
( PArray, PA
, valid
, nf
-- * Constructors
, empty
, singleton, singletonl
, replicate, replicatel, replicates, replicates'
, append, appendl
, concat, concatl
, unconcat
, nestUSegd
-- * Projections
, length, lengthl -- length from D.A.P.PArray.PData.Base
, index, indexl
, extract, extracts, extracts'
, slice, slicel
, takeUSegd
-- * Pack and Combine
, pack, packl
, packByTag
, combine2
-- * Enumerations
, enumFromTo, enumFromTol -- from D.A.P.PArray.Scalar
-- * Tuples
, zip, zipl
, zip3
, zip4
, zip5
, unzip, unzipl
-- * Conversions
, fromVector, toVector
, fromList, toList
, fromUArray, toUArray -- from D.A.P.PArray.Scalar
, fromUArray2) -- from D.A.P.PArray.Scalar
where
import qualified Data.Array.Parallel.Pretty as T
import Data.Array.Parallel.PArray.PData
import Data.Array.Parallel.PArray.PRepr
import Data.Array.Parallel.PArray.Scalar
import Data.Array.Parallel.PArray.Reference
import GHC.Exts
import Data.Vector (Vector)
import Data.Array.Parallel.Base (Tag)
import qualified Data.Array.Parallel.Array as A
import qualified Data.Array.Parallel.Unlifted as U
import qualified Data.Vector as V
import qualified "dph-lifted-base" Data.Array.Parallel.PArray as R
import qualified "dph-lifted-base" Data.Array.Parallel.PArray.Reference as R
import qualified Prelude as P
import Prelude hiding
( length, replicate, concat
, enumFromTo
, zip, zip3, unzip)
-- Pretty ---------------------------------------------------------------------
instance PA a => T.PprPhysical (PArray a) where
pprp (PArray n# pdata)
= ( T.text "PArray " T.<+> T.int (I# n#))
T.$+$ ( T.nest 4
$ pprpDataPA pdata)
instance PA a => Similar a where
similar = similarPA
instance PA a => R.PprPhysical1 a where
pprp1 = pprpPA
trace :: String -> a -> a
trace _str x
= x -- Debug.Trace.trace ("! " ++ str) x
-- Array -----------------------------------------------------------------------
-- Generic interface to PArrays.
--
-- NOTE:
-- The toVector conversion is defined by looking up every index instead of
-- using the bulk fromVectorPA function.
-- We do this to convert arrays of type (PArray Void) properly, as although a
-- (PArray Void) has an intrinsic length, a (PData Void) does not. If we try
-- to se the fromVectorPA function at this type we'll just get an `error`.
-- Arrays of type PArray Void aren't visible in the user API, but during
-- debugging we need to be able to print them out with the implied length.
--
instance PA e => A.Array PArray e where
valid = valid
singleton = singleton
append = append
length = length
index (PArray _ pdata) ix
= indexPA pdata ix
toVector arr = V.map (A.index arr) $ V.enumFromTo 0 (A.length arr - 1)
fromVector = fromVector
-- Operators ==================================================================
-- Each of these operators is wrapped in withRef functions so that we can
-- compare their outputs to the reference implementation.
-- See D.A.P.Reference for details.
-- Basics ---------------------------------------------------------------------
instance (Eq a, PA a) => Eq (PArray a) where
(==) (PArray _ xs) (PArray _ ys) = toVectorPA xs == toVectorPA ys
(/=) (PArray _ xs) (PArray _ ys) = toVectorPA xs /= toVectorPA ys
-- | Check that an array has a valid internal representation.
valid :: PA a => PArray a -> Bool
valid (PArray n# darr1)
= trace "valid"
$ validPA darr1
&& coversPA True darr1 (I# n#)
{-# INLINE_PA valid #-}
-- | Force an array to normal form.
nf :: PA a => PArray a -> ()
nf (PArray _ d)
= trace "nf"
$ nfPA d
{-# INLINE_PA nf #-}
-- Constructors ----------------------------------------------------------------
-- | O(1). An empty array.
empty :: PA a => PArray a
empty
= withRef1 "empty" R.empty
$ PArray 0# emptyPA
{-# INLINE_PA empty #-}
-- | O(1). Produce an array containing a single element.
singleton :: PA a => a -> PArray a
singleton x
= withRef1 "singleton" (R.singleton x)
$ PArray 1# (replicatePA 1 x)
{-# INLINE_PA singleton #-}
-- | O(n). Produce an array of singleton arrays.
singletonl :: PA a => PArray a -> PArray (PArray a)
singletonl arr
= withRef2 "singletonl" (R.singletonl (toRef1 arr))
$ replicatel_ (replicate_ (length arr) 1) arr
{-# INLINE_PA singletonl #-}
-- | O(n). Define an array of the given size, that maps all elements to the same value.
-- We require the replication count to be > 0 so that it's easier to maintain
-- the validPR invariants for nested arrays.
replicate :: PA a => Int -> a -> PArray a
replicate n x
= withRef1 "replicate" (R.replicate n x)
$ replicate_ n x
{-# INLINE_PA replicate #-}
replicate_ :: PA a => Int -> a -> PArray a
replicate_ (I# n#) x
= PArray n# (replicatePA (I# n#) x)
{-# INLINE_PA replicate_ #-}
-- | O(sum lengths). Lifted replicate.
replicatel :: PA a => PArray Int -> PArray a -> PArray (PArray a)
replicatel reps arr
= withRef2 "replicatel" (R.replicatel (toRef1 reps) (toRef1 arr))
$ replicatel_ reps arr
replicatel_ :: PA a => PArray Int -> PArray a -> PArray (PArray a)
replicatel_ (PArray n# (PInt lens)) (PArray _ pdata)
= if n# ==# 0# then empty else
let !segd = U.lengthsToSegd lens
!vsegd = U.promoteSegdToVSegd segd
!pdata' = replicatesPA segd pdata
!pdatas' = singletondPA pdata'
in PArray n# $ mkPNestedPA vsegd pdatas' segd pdata'
{-# INLINE_PA replicatel_ #-}
-- | O(sum lengths). Segmented replicate.
replicates :: PA a => U.Segd -> PArray a -> PArray a
replicates segd arr@(PArray _ pdata)
= trace (T.render $ T.text "!!! replicates " T.$+$ T.pprp segd T.$+$ T.pprp arr)
$ withRef1 "replicates" (R.replicates segd (toRef1 arr))
$ let !(I# n#) = U.elementsSegd segd
in PArray n# $ replicatesPA segd pdata
{-# INLINE_PA replicates #-}
-- | O(sum lengths). Wrapper for segmented replicate that takes replication counts
-- and uses them to build the `U.Segd`.
replicates' :: PA a => PArray Int -> PArray a -> PArray a
replicates' (PArray _ (PInt reps)) arr
= trace "replicates'"
$ replicates (U.lengthsToSegd reps) arr
{-# INLINE_PA replicates' #-}
-- | Append two arrays.
append :: PA a => PArray a -> PArray a -> PArray a
append arr1@(PArray n1# darr1) arr2@(PArray n2# darr2)
= withRef1 "append" (R.append (toRef1 arr1) (toRef1 arr2))
$ PArray (n1# +# n2#) (appendPA darr1 darr2)
{-# INLINE_PA append #-}
-- | Lifted append.
-- Both arrays must have the same length
appendl :: PA a => PArray (PArray a) -> PArray (PArray a) -> PArray (PArray a)
appendl arr1@(PArray n# pdata1) arr2@(PArray _ pdata2)
= withRef2 "appendl" (R.appendl (toRef2 arr1) (toRef2 arr2))
$ PArray n# $ appendlPA pdata1 pdata2
{-# INLINE_PA appendl #-}
-- | Concatenate a nested array.
concat :: PA a => PArray (PArray a) -> PArray a
concat arr@(PArray _ darr)
= withRef1 "concat" (R.concat (toRef2 arr))
$ let darr' = concatPA darr
!(I# n#) = lengthPA darr'
in PArray n# darr'
{-# INLINE_PA concat #-}
-- | Lifted concat.
concatl :: PA a => PArray (PArray (PArray a)) -> PArray (PArray a)
concatl arr@(PArray n# pdata1)
= withRef2 "concatl" (R.concatl (toRef3 arr))
$ PArray n# $ concatlPA pdata1
{-# INLINE_PA concatl #-}
-- | Impose a nesting structure on a flat array
unconcat :: (PA a, PA b) => PArray (PArray a) -> PArray b -> PArray (PArray b)
unconcat (PArray n# pdata1) (PArray _ pdata2)
= trace "! unconcat"
$ PArray n# $ unconcatPA pdata1 pdata2
{-# INLINE_PA unconcat #-}
-- | Create a nested array from a segment descriptor and some flat data.
-- The segment descriptor must represent as many elements as present
-- in the flat data array, else `error`
nestUSegd :: PA a => U.Segd -> PArray a -> PArray (PArray a)
nestUSegd segd (PArray n# pdata)
| U.elementsSegd segd == I# n#
, I# n2# <- U.lengthSegd segd
= PArray n2#
$ PNested (U.promoteSegdToVSegd segd) (singletondPA pdata) segd pdata
| otherwise
= error $ unlines
[ "Data.Array.Parallel.PArray.nestUSegd: number of elements defined by "
++ "segment descriptor and data array do not match"
, " length of segment desciptor = " ++ show (U.elementsSegd segd)
, " length of data array = " ++ show (I# n#) ]
{-# INLINE_PA nestUSegd #-}
-- Projections ---------------------------------------------------------------
-- | Take the length of some arrays.
lengthl :: PA a => PArray (PArray a) -> PArray Int
lengthl arr@(PArray n# (PNested vsegd _ _ _))
= withRef1 "lengthl" (R.lengthl (toRef2 arr))
$ PArray n# $ PInt $ U.takeLengthsOfVSegd vsegd
{-# INLINE_PA lengthl #-}
-- | O(1). Lookup a single element from the source array.
index :: PA a => PArray a -> Int -> a
index (PArray _ arr) ix
= trace "index"
$ indexPA arr ix
{-# INLINE_PA index #-}
-- | O(len indices). Lookup a several elements from several source arrays
indexl :: PA a => PArray (PArray a) -> PArray Int -> PArray a
indexl (PArray n# darr) (PArray _ ixs)
= trace "indexl"
$ PArray n# (indexlPA darr ixs)
{-# INLINE_PA indexl #-}
-- | Extract a range of elements from an array.
extract :: PA a => PArray a -> Int -> Int -> PArray a
extract (PArray _ arr) start len@(I# len#)
= trace "extract"
$ PArray len# (extractPA arr start len)
{-# INLINE_PA extract #-}
-- | Segmented extract.
extracts :: PA a => Vector (PArray a) -> U.SSegd -> PArray a
extracts arrs ssegd
= trace "extracts"
$ let pdatas = fromVectordPA $ V.map (\(PArray _ vec) -> vec) arrs
!(I# n#) = (U.sum $ U.lengthsOfSSegd ssegd)
in PArray n#
(extractssPA pdatas ssegd)
{-# INLINE_PA extracts #-}
-- | Wrapper for `extracts` that takes arrays of sources, starts and lengths of
-- the segments, and uses these to build the `U.SSegd`.
-- TODO: The lengths of the sources, starts and lengths arrays must be the same,
-- but this is not checked.
-- All sourceids must point to valid data arrays.
-- Segments must be within their corresponding source array.
extracts'
:: PA a
=> Vector (PArray a)
-> PArray Int -- ^ id of source array for each segment.
-> PArray Int -- ^ starting index of each segment in its source array.
-> PArray Int -- ^ length of each segment.
-> PArray a
extracts' arrs (PArray _ (PInt sources)) (PArray _ (PInt starts)) (PArray _ (PInt lengths))
= trace "extracts'"
$ let segd = U.lengthsToSegd lengths
ssegd = U.mkSSegd starts sources segd
in extracts arrs ssegd
{-# INLINE_PA extracts' #-}
-- | Extract a range of elements from an arrary.
-- Like `extract` but with the parameters in a different order.
slice :: PA a => Int -> Int -> PArray a -> PArray a
slice start len@(I# len#) (PArray _ darr)
= trace "slice"
$ PArray len# (extractPA darr start len)
{-# INLINE_PA slice #-}
-- | Extract some slices from some arrays.
-- The arrays of starting indices and lengths must themselves
-- have the same length.
slicel :: PA a => PArray Int -> PArray Int -> PArray (PArray a) -> PArray (PArray a)
slicel (PArray n# sliceStarts) (PArray _ sliceLens) (PArray _ darr)
= trace "slicel"
$ PArray n# (slicelPA sliceStarts sliceLens darr)
{-# INLINE_PA slicel #-}
-- | Take the segment descriptor from a nested array and demote it to a
-- plain Segd. This is unsafe because it can cause index space overflow.
takeUSegd :: PArray (PArray a) -> U.Segd
takeUSegd (PArray _ pdata)
= trace "takeUSegd"
$ takeSegdPD pdata
{-# INLINE_PA takeUSegd #-}
-- Pack and Combine -----------------------------------------------------------
-- | Select the elements of an array that have their tag set to True.
pack :: PA a => PArray a -> PArray Bool -> PArray a
pack arr@(PArray _ xs) flags@(PArray _ (PBool sel2))
= withRef1 "pack" (R.pack (toRef1 arr) (toRef1 flags))
$ let darr' = packByTagPA xs (U.tagsSel2 sel2) 1
-- The selector knows how many elements are set to '1',
-- so we can use this for the length of the resulting array.
!(I# m#) = U.elementsSel2_1 sel2
in PArray m# darr'
{-# INLINE_PA pack #-}
-- | Lifted pack.
packl :: PA a => PArray (PArray a) -> PArray (PArray Bool) -> PArray (PArray a)
packl xss@(PArray n# xdata@(PNested _ _ segd _))
fss@(PArray _ fdata)
= withRef2 "packl" (R.packl (toRef2 xss) (toRef2 fss))
$ let
-- Concatenate both arrays to get the flat data.
-- Although the virtual segmentation should be the same,
-- the physical segmentation of both arrays may be different.
xdata_flat = concatPA xdata
PBool sel = concatPA fdata
tags = U.tagsSel2 sel
-- Count how many elements go into each segment.
segd' = U.lengthsToSegd $ U.count_s segd tags 1
-- Build the result array
vsegd' = U.promoteSegdToVSegd segd'
flat' = packByTagPA xdata_flat tags 1
pdatas' = singletondPA flat'
in PArray n# (PNested vsegd' pdatas' segd' flat')
{-# INLINE_PA packl #-}
-- | Filter an array based on some tags.
packByTag :: PA a => PArray a -> U.Array Tag -> Tag -> PArray a
packByTag arr@(PArray _ darr) tags tag
= withRef1 "packByTag" (R.packByTag (toRef1 arr) tags tag)
$ let darr' = packByTagPA darr tags tag
!(I# n#) = lengthPA darr'
in PArray n# darr'
{-# INLINE_PA packByTag #-}
-- | Combine two arrays based on a selector.
combine2 :: forall a. PA a => U.Sel2 -> PArray a -> PArray a -> PArray a
combine2 sel arr1@(PArray _ darr1) arr2@(PArray _ darr2)
= withRef1 "combine2" (R.combine2 sel (toRef1 arr1) (toRef1 arr2))
$ let darr' = combine2PA sel darr1 darr2
!(I# n#) = lengthPA darr'
in PArray n# darr'
{-# INLINE_PA combine2 #-}
-- Tuples ---------------------------------------------------------------------
-- | O(1). Zip a pair of arrays into an array of pairs.
-- The two arrays must have the same length, else `error`.
zip :: PArray a -> PArray b -> PArray (a, b)
zip (PArray n# pdata1) (PArray _ pdata2)
= trace "zip"
$ PArray n# $ zipPD pdata1 pdata2
{-# INLINE_PA zip #-}
-- | Lifted zip.
zipl :: (PA a, PA b)
=> PArray (PArray a) -> PArray (PArray b) -> PArray (PArray (a, b))
zipl (PArray n# xs) (PArray _ ys)
= trace "zipl"
$ PArray n# $ ziplPA xs ys
{-# INLINE_PA zipl #-}
-- | O(1). Zip three arrays.
-- All arrays must have the same length, else `error`.
zip3 :: PArray a -> PArray b -> PArray c -> PArray (a, b, c)
zip3 (PArray n# pdata1) (PArray _ pdata2) (PArray _ pdata3)
= trace "zip3"
$ PArray n# $ zip3PD pdata1 pdata2 pdata3
{-# INLINE_PA zip3 #-}
-- | O(1). Zip four arrays.
-- All arrays must have the same length, else `error`.
zip4 :: PArray a -> PArray b -> PArray c -> PArray d -> PArray (a, b, c, d)
zip4 (PArray n# pdata1) (PArray _ pdata2) (PArray _ pdata3) (PArray _ pdata4)
= trace "zip4"
$ PArray n# $ zip4PD pdata1 pdata2 pdata3 pdata4
{-# INLINE_PA zip4 #-}
-- | O(1). Zip five arrays.
-- All arrays must have the same length, else `error`.
zip5 :: PArray a -> PArray b -> PArray c -> PArray d -> PArray e -> PArray (a, b, c, d, e)
zip5 (PArray n# pdata1) (PArray _ pdata2) (PArray _ pdata3) (PArray _ pdata4) (PArray _ pdata5)
= trace "zip5"
$ PArray n# $ zip5PD pdata1 pdata2 pdata3 pdata4 pdata5
{-# INLINE_PA zip5 #-}
-- | O(1). Unzip an array of pairs into a pair of arrays.
unzip :: PArray (a, b) -> (PArray a, PArray b)
unzip (PArray n# (PTuple2 xs ys))
= trace "unzip"
$ (PArray n# xs, PArray n# ys)
{-# INLINE_PA unzip #-}
-- | Lifted unzip
unzipl :: PArray (PArray (a, b)) -> PArray (PArray a, PArray b)
unzipl (PArray n# pdata)
= trace "unzipl"
$ PArray n# $ unziplPD pdata
{-# INLINE_PA unzipl #-}
-- Conversions ----------------------------------------------------------------
-- | Convert a `Vector` to a `PArray`
fromVector :: PA a => Vector a -> PArray a
fromVector vec
= trace "fromVector"
$ let !(I# n#) = V.length vec
in PArray n# (fromVectorPA vec)
{-# INLINE_PA fromVector #-}
-- | Convert a `PArray` to a `Vector`
toVector :: PA a => PArray a -> Vector a
toVector (PArray _ arr)
= trace "toVector"
$ toVectorPA arr
{-# INLINE_PA toVector #-}
-- | Convert a list to a `PArray`.
fromList :: PA a => [a] -> PArray a
fromList xx
= trace "fromList"
$ let !(I# n#) = P.length xx
in PArray n# (fromVectorPA $ V.fromList xx)
{-# INLINE_PA fromList #-}
-- | Convert a `PArray` to a list.
toList :: PA a => PArray a -> [a]
toList (PArray _ arr)
= trace "toList"
$ V.toList $ toVectorPA arr
{-# INLINE_PA toList #-}