packages feed

accelerate-arithmetic-0.0.1: benchmark/CUBLASBatched.hs

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module CUBLASBatched where

import qualified Data.Array.Accelerate.Arithmetic.LinearAlgebra as ALinAlg

import qualified Data.Array.Accelerate.Utility.Lift.Acc as Acc
import Data.Array.Accelerate.Utility.Lift.Acc (acc, expr)

import Data.Array.Accelerate.Array.Sugar (EltRepr)
import Data.Array.Accelerate (Array, DIM3, Acc, Z (..), (:.) (..), Exp)
import qualified Data.Array.Accelerate.CUDA.Foreign as AF
import qualified Data.Array.Accelerate.CUDA as AC
import qualified Data.Array.Accelerate as A

import qualified Foreign.CUDA.Cublas as Cublas
import Foreign.CUDA.Ptr (DevicePtr, castDevPtr, advanceDevPtr)

import Foreign.C.Types (CFloat, CDouble)
import Foreign.Storable (Storable)

import Data.Tuple.HT (uncurry3)


type Matrix ix = Array (ix :. Int :. Int)
type Vector ix = Array (ix :. Int)
type Scalar ix = Array ix

mul ::
   (A.Shape ix, A.Slice ix, Eq ix, Element a, A.Elt a, A.IsNum a) =>
   Cublas.Handle ->
   Exp a ->
   ALinAlg.Matrix ix a -> ALinAlg.Matrix ix a ->
   ALinAlg.Matrix ix a
mul handle alpha a b =
   A.foreignAcc
      (AF.CUDAForeignAcc "mul" $ uncurry3 $ mulPlain handle)
      (Acc.modify (expr,acc,acc) $ \(alpha0, a0, b0) ->
         A.map (alpha0 *) $
         ALinAlg.multiplyMatrixMatrix a0 b0)
   $
   A.lift (A.unit alpha, a, b)

mulPlain ::
   (A.Shape ix, Eq ix, Element a, A.Elt a) =>
   Cublas.Handle ->
   A.Scalar a -> Matrix ix a -> Matrix ix a ->
   AF.CIO (Matrix ix a)
mulPlain handle alpha a b = do
   let (aNumMatrices :. n  :. k) = A.arrayShape a
   let (bNumMatrices :. _k :. m) = A.arrayShape b
   let numMatrices =
          if aNumMatrices == bNumMatrices
            then aNumMatrices
            else error "mul: mismatching shapes of matrix arrays"
   c <- AF.allocateArray (numMatrices :. n :. m)
   (pas, lda) <- arrayPtrs a
   (pbs, ldb) <- arrayPtrs b
   (pcs, ldc) <- arrayPtrs c
   AF.liftIO $
      Cublas.gemmBatched handle Cublas.N Cublas.N m n k
         (storableFromScalar alpha)
         pbs ldb
         pas lda
         0
         pcs ldc
         (A.arraySize numMatrices)
   return c

mac ::
   (A.Shape ix, A.Slice ix, Eq ix, Element a, A.Elt a, A.IsNum a) =>
   Cublas.Handle ->
   Exp a -> ALinAlg.Matrix ix a -> ALinAlg.Matrix ix a ->
   Exp a -> ALinAlg.Matrix ix a ->
   ALinAlg.Matrix ix a
mac handle alpha a b beta c =
   A.foreignAcc
      (AF.CUDAForeignAcc "mac" $
       \((alpha0, a0, b0), (beta0, c0)) ->
          macPlain handle alpha0 a0 b0 beta0 c0)
      (Acc.modify ((expr,acc,acc),(expr,acc)) $
       \((alpha0, a0, b0), (beta0, c0)) ->
         A.zipWith (+)
            (A.map (alpha0 *) $
             ALinAlg.multiplyMatrixMatrix a0 b0)
            (A.map (beta0 *) c0))
   $
   A.lift ((A.unit alpha, a, b), (A.unit beta, c))

macPlain ::
   (A.Shape ix, Eq ix, Element a, A.Elt a) =>
   Cublas.Handle ->
   A.Scalar a -> Matrix ix a -> Matrix ix a ->
   A.Scalar a -> Matrix ix a ->
   AF.CIO (Matrix ix a)
macPlain handle alpha a b beta c = do
   let (aNumMatrices :. n  :. k ) = A.arrayShape a
   let (bNumMatrices :. _k :. m ) = A.arrayShape b
   let (cNumMatrices :. n' :. m') = A.arrayShape c
   let numMatrices =
          if aNumMatrices == bNumMatrices
             &&
             aNumMatrices == cNumMatrices
            then aNumMatrices
            else error "mac: mismatching shapes of matrix arrays"
   d <- AF.allocateArray (numMatrices :. n' :. m')
   AF.copyArray c d
   (pas, lda) <- arrayPtrs a
   (pbs, ldb) <- arrayPtrs b
   (pds, ldd) <- arrayPtrs d
   AF.liftIO $
      Cublas.gemmBatched handle Cublas.N Cublas.N m n k
         (storableFromScalar alpha)
         pbs ldb
         pas lda
         (storableFromScalar beta)
         pds ldd
         (A.arraySize numMatrices)
   return d

lu ::
   (A.Shape ix, Eq ix, Element a, A.Elt a) =>
   Cublas.Handle ->
   ALinAlg.Matrix ix a ->
   (ALinAlg.Matrix ix a, ALinAlg.Vector ix Int, ALinAlg.Scalar ix Int)
lu handle =
   A.unlift
   .
   A.foreignAcc
      (AF.CUDAForeignAcc "lu" $ luPlain handle)
      (error "Requires CUDA backend")

luPlain ::
   (A.Shape ix, Eq ix, Element a, A.Elt a) =>
   Cublas.Handle ->
   Matrix ix a ->
   AF.CIO (Matrix ix a, Vector ix Int, Scalar ix Int)
luPlain handle a = do
   let sh@(numMatrices :. n  :. k) = A.arrayShape a
   let size =
          if n == k
            then n
            else error "lu: matrices must have square shape"
   b <- AF.allocateArray sh
   AF.copyArray a b
   (pbs, ldb) <- arrayPtrs b

   pivot <- AF.allocateArray (numMatrices :. size)
   pivotPtr <- fmap (castDevPtr . snd) $ AF.devicePtrsOfArray pivot

   info <- AF.allocateArray numMatrices
   infoPtr <- fmap (castDevPtr . snd) $ AF.devicePtrsOfArray info

   AF.liftIO $
      Cublas.getrfBatched handle size
         pbs ldb
         pivotPtr infoPtr
         (A.arraySize numMatrices)
   return (b, pivot, info)


luInv ::
   (A.Shape ix, Eq ix, Element a, A.Elt a) =>
   Cublas.Handle ->
   (ALinAlg.Matrix ix a, ALinAlg.Vector ix Int, ALinAlg.Scalar ix Int) ->
   ALinAlg.Matrix ix a
luInv handle =
   A.foreignAcc
      (AF.CUDAForeignAcc "luInv" $ luInvPlain handle)
      (error "Requires CUDA backend")
   .
   A.lift

luInvPlain ::
   (A.Shape ix, Eq ix, Element a, A.Elt a) =>
   Cublas.Handle ->
   (Matrix ix a, Vector ix Int, Scalar ix Int) ->
   AF.CIO (Matrix ix a)
luInvPlain handle (a, pivot, info) = do
   let sh@(numMatrices :. n  :. k) = A.arrayShape a
   let size =
          if n == k
            then n
            else error "luInv: matrices must have square shape"
   c <- AF.allocateArray sh
   AF.copyArray a c
   (pas, lda) <- arrayPtrs a
   (pcs, ldc) <- arrayPtrs c

   pivotPtr <- fmap (castDevPtr . snd) $ AF.devicePtrsOfArray pivot
   infoPtr <- fmap (castDevPtr . snd) $ AF.devicePtrsOfArray info

   AF.liftIO $
      Cublas.getriBatched handle size
         pas lda
         pivotPtr
         pcs ldc
         infoPtr
         (A.arraySize numMatrices)
   return c


inv ::
   (A.Shape ix, Eq ix, Element a, A.Elt a) =>
   Cublas.Handle ->
   ALinAlg.Matrix ix a ->
   (ALinAlg.Matrix ix a, ALinAlg.Scalar ix Int)
inv handle a =
   let sol@(_,_,info) = lu handle a
   in  (luInv handle sol, info)


type Element a =
        (AF.DevicePtrs (EltRepr a) ~ ((), DevicePtr a),
         Fractional (StorableOf a),
         Cublas.Cublas (StorableOf a),
         Storable (StorableOf a),
         Real a)

type family StorableOf float
type instance StorableOf Float = CFloat
type instance StorableOf Double = CDouble

storableFromScalar ::
   (Real a, StorableOf a ~ b, Fractional b) => A.Scalar a -> b
storableFromScalar x = realToFrac $ A.indexArray x Z

arrayPtrs ::
   (Storable a, StorableOf e ~ a,
    A.Shape ix,
    AF.DevicePtrs (EltRepr e) ~ ((), DevicePtr e)) =>
   Array (ix :. Int :. Int) e -> AF.CIO ([DevicePtr a], Int)
arrayPtrs arr = do
   let (numMatrices :. n  :. k) = A.arrayShape arr
   pa <- fmap (castDevPtr . snd) $ AF.devicePtrsOfArray arr
   return (genPointers (n*k) pa (A.arraySize numMatrices), k)

genPointers ::
   (Storable a) =>
   Int -> DevicePtr a -> Int -> [DevicePtr a]
genPointers size p n =
   take n $ iterate (flip advanceDevPtr size) p


genMatrices :: (Acc (Array DIM3 Double), Acc (Array DIM3 Double))
genMatrices = (a,b)
   where
   a = A.generate (A.constant sha) $ \ix ->
      let (Z :. i :. j :. k) = unlift ix
      in A.fromIntegral (i+j+k)
   b = A.generate (A.constant shb) $ \ix ->
      let (Z :. i :. j :. k) = unlift ix
      in A.fromIntegral (i+j+k)
   numMats = 100 :: Int
   sha = Z :. numMats :. (3 :: Int) :. (4 :: Int)
   shb = Z :. numMats :. (4 :: Int) :. (2 :: Int)
   unlift :: Exp (Z :. Int :. Int :. Int)
      -> Z :. Exp Int :. Exp Int :. Exp Int
   unlift = A.unlift

test :: IO ()
test = do
   handle <- Cublas.create
   print genMatrices
   print $ AC.run $
      case genMatrices of
         (a,b) -> mul handle 1 a b