packages feed

accelerate-arithmetic-0.0: benchmark/NewtonInverse.hs

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module Main where

import qualified CUBLASBatched as Batched
import qualified Foreign.CUDA.Cublas as Cublas

import qualified Data.Array.Accelerate.Arithmetic.LinearAlgebra as ALinAlg
import qualified Data.Array.Accelerate.Utility.Loop as Loop
import qualified Data.Array.Accelerate.CUDA as CUDA
import qualified Data.Array.Accelerate as A
import Data.Array.Accelerate (All(All), Z(Z), (:.)((:.)))

import qualified Control.Concurrent.PooledIO.Independent as Pooled

import qualified Data.Packed.Matrix as Matrix
import qualified Data.Packed.Vector as Vector
import qualified Numeric.Container as Container
import qualified Numeric.LinearAlgebra.Algorithms as HMLinAlg

import Numeric.Container (Container, (<>))
import Data.Packed.Matrix (Matrix)
import Data.Packed.Vector (Vector)

import qualified System.Random as Rnd
import System.TimeIt (timeIt)

import Text.Printf (printf)

import qualified Data.List.HT as ListHT
import Data.Function.HT (nest)
import Data.Tuple.HT (mapPair)



newtonInverseStep ::
   (Num a, Container Vector a, Container.Product a) =>
   Matrix a -> Matrix a -> Matrix a
newtonInverseStep a x =
   Container.sub
      (Container.scale 2 x)
      (x <> a <> x)

newtonInverse ::
   (Num a, Container Vector a, Container.Product a) =>
   Int -> Matrix a -> Matrix a -> Matrix a
newtonInverse count start a =
   nest count (newtonInverseStep a) start


newtonInverseCUBLASStep, newtonInverseCUBLASStepMul ::
   (A.Shape ix, A.Slice ix, Eq ix, Batched.Element a, A.IsNum a, A.Elt a) =>
   Cublas.Handle ->
   ALinAlg.Matrix ix a ->
   ALinAlg.Matrix ix a ->
   ALinAlg.Matrix ix a
newtonInverseCUBLASStep h a x =
   Batched.mac h (-1) x (Batched.mul h 1 a x) 2 x

newtonInverseCUBLASStepMul h a x =
   A.zipWith (-) (A.map (2*) x) $
   Batched.mul h 1 x $ Batched.mul h 1 a x

newtonInverseCUBLAS ::
   (A.Shape ix, A.Slice ix, Eq ix, Batched.Element a, A.IsNum a, A.Elt a) =>
   Cublas.Handle ->
   A.Exp Int ->
   ALinAlg.Matrix ix a ->
   ALinAlg.Matrix ix a ->
   ALinAlg.Matrix ix a
newtonInverseCUBLAS h n seed a =
   Loop.nest n (newtonInverseCUBLASStep h a) seed


randomMatrixInv :: Int -> (Matrix Double, Matrix Double)
randomMatrixInv size =
   let x =
          Matrix.fromLists $ take size $ ListHT.sliceVertical size $
          Rnd.randomRs (-1,1::Double) $ Rnd.mkStdGen 42
   in  (x, HMLinAlg.inv x)


parallel :: [a] -> (Int -> a -> IO ()) -> IO ()
parallel xs f = Pooled.run $ zipWith f [0 ..] xs

disturbedMatrices ::
   (Container Vector a) =>
   Matrix a -> [a] -> [Matrix a]
disturbedMatrices x yelems =
   let size = Matrix.rows x
   in  map (Container.add x . Matrix.fromLists) $
       ListHT.sliceVertical size $
       ListHT.sliceVertical size $
       yelems

mainHMatrixDirect ::
   (Show a, Container Vector a, HMLinAlg.Field a) =>
   String ->
   Int -> (Matrix a, Matrix a) -> [a] -> IO ()
mainHMatrixDirect typ numberOfMatrices (x, _xinv) yelems = do
   let yinvs = map HMLinAlg.inv $ disturbedMatrices x yelems
   putStrLn $ "hmatrix-direct-" ++ typ
   timeIt $ parallel (take numberOfMatrices yinvs) $ \ n y ->
      writeFile (printf "/tmp/hmatrix-direct-%s%03d.txt" typ n) $ show y

mainHMatrix ::
   (Show a, Container Vector a, Container.Product a) =>
   String ->
   Int -> Int -> (Matrix a, Matrix a) -> [a] -> IO ()
mainHMatrix typ numberOfMatrices newtonIts (x, xinv) yelems = do
   let yinvs = map (newtonInverse newtonIts xinv) $ disturbedMatrices x yelems
   putStrLn $ "hmatrix-" ++ typ
   timeIt $ parallel (take numberOfMatrices yinvs) $ \ n y ->
      writeFile (printf "/tmp/hmatrix-%s%03d.txt" typ n) $ show y


mainCUDA ::
   (A.Elt a, A.IsNum a, Container.Element a) =>
   String ->
   Int -> Int -> (Matrix a, Matrix a) -> [a] -> IO ()
mainCUDA typ numberOfMatrices newtonIts (xm, xinvm) yelems = do
   let size = Matrix.rows xm
       matrixAccFromHM =
          A.fromList (Z :. size :. size) .
          Vector.toList . Matrix.flatten
       xarr = matrixAccFromHM xm
       xinvarr = matrixAccFromHM xinvm

   let ysarr =
          A.fromList (Z :. numberOfMatrices :. size :. size) yelems
       rep = A.replicate (A.lift $ Z :. numberOfMatrices :. All :. All)
       yinvs =
          CUDA.run1
             (\args ->
                case A.unlift args of
                   (x, xinv, ys) ->
                      ALinAlg.newtonInverse (A.constant newtonIts) (rep xinv) $
                      A.zipWith (+) ys (rep x))
             (xarr, xinvarr, ysarr)

   putStrLn $ "cuda-" ++ typ
   timeIt $ writeFile ("/tmp/cuda-"++typ++".txt") $ show yinvs


mainCUBLASDirect ::
   (Batched.Element a, Container.Element a, A.IsNum a, A.Elt a) =>
   String ->
   Int -> (Matrix a, Matrix a) -> [a] -> IO ()
mainCUBLASDirect typ numberOfMatrices (xm, _xinvm) yelems = do
   let size = Matrix.rows xm
       matrixAccFromHM =
          A.fromList (Z :. size :. size) .
          Vector.toList . Matrix.flatten
       xarr = matrixAccFromHM xm

   handle <- Cublas.create
   let ysarr =
          A.fromList (Z :. numberOfMatrices :. size :. size) yelems
       rep = A.replicate (A.lift $ Z :. numberOfMatrices :. All :. All)
       yinvs =
          CUDA.run1
             (\args ->
                case A.unlift args of
                   (x, ys) ->
                      fst $ Batched.inv handle $ A.zipWith (+) ys (rep x))
             (xarr, ysarr)

   putStrLn $ "cublas-direct-" ++ typ
   timeIt $ writeFile ("/tmp/cublas-direct-"++typ++".txt") $ show yinvs


mainCUBLAS ::
   (Batched.Element a, Container.Element a, A.IsNum a, A.Elt a) =>
   String ->
   Int -> Int -> (Matrix a, Matrix a) -> [a] -> IO ()
mainCUBLAS typ numberOfMatrices newtonIts (xm, xinvm) yelems = do
   let size = Matrix.rows xm
       matrixAccFromHM =
          A.fromList (Z :. size :. size) .
          Vector.toList . Matrix.flatten
       xarr = matrixAccFromHM xm
       xinvarr = matrixAccFromHM xinvm

   handle <- Cublas.create
   let ysarr =
          A.fromList (Z :. numberOfMatrices :. size :. size) yelems
       rep = A.replicate (A.lift $ Z :. numberOfMatrices :. All :. All)
       yinvs =
          CUDA.run1
             (\args ->
                case A.unlift args of
                   (x, xinv, ys) ->
                      newtonInverseCUBLAS handle (A.constant newtonIts) (rep xinv) $
                      A.zipWith (+) ys (rep x))
             (xarr, xinvarr, ysarr)

   putStrLn $ "cublas-" ++ typ
   timeIt $ writeFile ("/tmp/cublas-"++typ++".txt") $ show yinvs


main :: IO ()
main = do
   let n = 96
   let sz = 50
   let its = 20
   let xmsDouble = randomMatrixInv sz
       ysDouble = Rnd.randomRs (-0.01,0.01::Double) $ Rnd.mkStdGen 23
   let xmsFloat =
          mapPair
             (Container.cmap realToFrac, Container.cmap realToFrac)
             xmsDouble
       ysFloat :: [Float]
       ysFloat = map realToFrac ysDouble
   mainHMatrixDirect "double" n xmsDouble ysDouble
--   mainHMatrixDirect "float" n xmsFloat ysFloat
   mainHMatrix "double" n its xmsDouble ysDouble
   mainHMatrix "float" n its xmsFloat ysFloat
   mainCUBLASDirect "double" n xmsDouble ysDouble
   mainCUBLASDirect "float" n xmsFloat ysFloat
   mainCUBLAS "double" n its xmsDouble ysDouble
   mainCUBLAS "float" n its xmsFloat ysFloat
   mainCUDA "double" n its xmsDouble ysDouble
   mainCUDA "float" n its xmsFloat ysFloat