1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
{-# LANGUAGE BangPatterns, ExplicitForAll, TypeOperators, MagicHash #-}
{-# OPTIONS -fno-warn-orphans #-}
module Data.Array.Repa.Operators.Reduction
( foldS, foldP
, foldAllS, foldAllP
, sumS, sumP
, sumAllS, sumAllP
, equalsS, equalsP)
where
import Data.Array.Repa.Base
import Data.Array.Repa.Index
import Data.Array.Repa.Eval
import Data.Array.Repa.Repr.Unboxed
import Data.Array.Repa.Operators.Mapping as R
import Data.Array.Repa.Shape as S
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Unboxed.Mutable as M
import Prelude hiding (sum)
import qualified Data.Array.Repa.Eval.Reduction as E
import System.IO.Unsafe
import GHC.Exts
-- fold ----------------------------------------------------------------------
-- | Sequential reduction of the innermost dimension of an arbitrary rank array.
--
-- Combine this with `transpose` to fold any other dimension.
--
-- Elements are reduced in the order of their indices, from lowest to highest.
-- Applications of the operator are associatied arbitrarily.
--
-- >>> let c 0 x = x; c x 0 = x; c x y = y
-- >>> let a = fromListUnboxed (Z :. 2 :. 2) [1,2,3,4] :: Array U (Z :. Int :. Int) Int
-- >>> foldS c 0 a
-- AUnboxed (Z :. 2) (fromList [2,4])
--
foldS :: (Shape sh, Source r a, Unbox a)
=> (a -> a -> a)
-> a
-> Array r (sh :. Int) a
-> Array U sh a
foldS f z arr
= arr `deepSeqArray`
let sh@(sz :. n') = extent arr
!(I# n) = n'
in unsafePerformIO
$ do mvec <- M.unsafeNew (S.size sz)
E.foldS mvec (\ix -> arr `unsafeIndex` fromIndex sh (I# ix)) f z n
!vec <- V.unsafeFreeze mvec
now $ fromUnboxed sz vec
{-# INLINE [1] foldS #-}
-- | Parallel reduction of the innermost dimension of an arbitray rank array.
--
-- The first argument needs to be an associative sequential operator.
-- The starting element must be neutral with respect to the operator, for
-- example @0@ is neutral with respect to @(+)@ as @0 + a = a@.
-- These restrictions are required to support parallel evaluation, as the
-- starting element may be used multiple times depending on the number of threads.
--
-- Elements are reduced in the order of their indices, from lowest to highest.
-- Applications of the operator are associatied arbitrarily.
--
-- >>> let c 0 x = x; c x 0 = x; c x y = y
-- >>> let a = fromListUnboxed (Z :. 2 :. 2) [1,2,3,4] :: Array U (Z :. Int :. Int) Int
-- >>> foldP c 0 a
-- AUnboxed (Z :. 2) (fromList [2,4])
--
foldP :: (Shape sh, Source r a, Unbox a, Monad m)
=> (a -> a -> a)
-> a
-> Array r (sh :. Int) a
-> m (Array U sh a)
foldP f z arr
= arr `deepSeqArray`
let sh@(sz :. n) = extent arr
in case rank sh of
-- specialise rank-1 arrays, else one thread does all the work.
-- We can't match against the shape constructor,
-- otherwise type error: (sz ~ Z)
--
1 -> do
x <- foldAllP f z arr
now $ fromUnboxed sz $ V.singleton x
_ -> now
$ unsafePerformIO
$ do mvec <- M.unsafeNew (S.size sz)
E.foldP mvec (\ix -> arr `unsafeIndex` fromIndex sh ix) f z n
!vec <- V.unsafeFreeze mvec
now $ fromUnboxed sz vec
{-# INLINE [1] foldP #-}
-- foldAll --------------------------------------------------------------------
-- | Sequential reduction of an array of arbitrary rank to a single scalar value.
--
-- Elements are reduced in row-major order. Applications of the operator are
-- associated arbitrarily.
--
foldAllS :: (Shape sh, Source r a)
=> (a -> a -> a)
-> a
-> Array r sh a
-> a
foldAllS f z arr
= arr `deepSeqArray`
let !ex = extent arr
!(I# n) = size ex
in E.foldAllS
(\ix -> arr `unsafeIndex` fromIndex ex (I# ix))
f z n
{-# INLINE [1] foldAllS #-}
-- | Parallel reduction of an array of arbitrary rank to a single scalar value.
--
-- The first argument needs to be an associative sequential operator.
-- The starting element must be neutral with respect to the operator,
-- for example @0@ is neutral with respect to @(+)@ as @0 + a = a@.
-- These restrictions are required to support parallel evaluation, as the
-- starting element may be used multiple times depending on the number of threads.
--
-- Elements are reduced in row-major order. Applications of the operator are
-- associated arbitrarily.
--
foldAllP
:: (Shape sh, Source r a, Unbox a, Monad m)
=> (a -> a -> a)
-> a
-> Array r sh a
-> m a
foldAllP f z arr
= arr `deepSeqArray`
let sh = extent arr
n = size sh
in return
$ unsafePerformIO
$ E.foldAllP (\ix -> arr `unsafeIndex` fromIndex sh ix) f z n
{-# INLINE [1] foldAllP #-}
-- sum ------------------------------------------------------------------------
-- | Sequential sum the innermost dimension of an array.
sumS :: (Shape sh, Source r a, Num a, Unbox a)
=> Array r (sh :. Int) a
-> Array U sh a
sumS = foldS (+) 0
{-# INLINE [3] sumS #-}
-- | Parallel sum the innermost dimension of an array.
sumP :: (Shape sh, Source r a, Num a, Unbox a, Monad m)
=> Array r (sh :. Int) a
-> m (Array U sh a)
sumP = foldP (+) 0
{-# INLINE [3] sumP #-}
-- sumAll ---------------------------------------------------------------------
-- | Sequential sum of all the elements of an array.
sumAllS :: (Shape sh, Source r a, Num a)
=> Array r sh a
-> a
sumAllS = foldAllS (+) 0
{-# INLINE [3] sumAllS #-}
-- | Parallel sum all the elements of an array.
sumAllP :: (Shape sh, Source r a, Unbox a, Num a, Monad m)
=> Array r sh a
-> m a
sumAllP = foldAllP (+) 0
{-# INLINE [3] sumAllP #-}
-- Equality ------------------------------------------------------------------
instance (Shape sh, Eq sh, Source r a, Eq a) => Eq (Array r sh a) where
(==) arr1 arr2
= extent arr1 == extent arr2
&& (foldAllS (&&) True (R.zipWith (==) arr1 arr2))
-- | Check whether two arrays have the same shape and contain equal elements,
-- in parallel.
equalsP :: (Shape sh, Source r1 a, Source r2 a, Eq a, Monad m)
=> Array r1 sh a
-> Array r2 sh a
-> m Bool
equalsP arr1 arr2
= do same <- foldAllP (&&) True (R.zipWith (==) arr1 arr2)
return $ (extent arr1 == extent arr2) && same
-- | Check whether two arrays have the same shape and contain equal elements,
-- sequentially.
equalsS :: (Shape sh, Source r1 a, Source r2 a, Eq a)
=> Array r1 sh a
-> Array r2 sh a
-> Bool
equalsS arr1 arr2
= extent arr1 == extent arr2
&& (foldAllS (&&) True (R.zipWith (==) arr1 arr2))
|