arrayfire-0.6.0.0: Haskell bindings to the ArrayFire general-purpose GPU library

CopyrightDavid Johnson (c) 2019-2020
LicenseBSD3
MaintainerDavid Johnson <djohnson.m@gmail.com>
StabilityExperimental
PortabilityGHC
Safe HaskellNone
LanguageHaskell2010

ArrayFire

Contents

Description

Synopsis

Tutorial

ArrayFire is a high performance parallel computing library that features modules for statistical and numerical methods. Example usage is depicted below.

module Main where

import qualified ArrayFire as A

main :: IO ()
main = print $ A.matrix @Double (3,2) [[1,2,3],[4,5,6]]

Each Array is constructed and displayed in column-major order.

ArrayFire Array
[3 2 1 1]
   1.0000     4.0000
   2.0000     5.0000
   3.0000     6.0000

Modules

All child modules are re-exported top-level in the ArrayFire module. We recommend importing ArrayFire qualified so as to avoid naming collisions.

>>> import qualified ArrayFire as A

Exceptions

{-# LANGUAGE TypeApplications #-}
module Main where

import qualified ArrayFire         as A
import           Control.Exception ( catch )

main :: IO ()
main = A.printArray action `catch` (\(e :: A.AFException) -> print e)
  where
    action =
      A.matrix @Double (3,3) [[1..],[1..],[1..]]
        `A.mul` A.matrix @Double (2,2) [[1..],[1..]]

The above operation is invalid since the matrix multiply has improper dimensions. The caught exception produces the following error:

AFException {afExceptionType = SizeError, afExceptionCode = 203, afExceptionMsg = "Invalid input size"}

Construction

An Array can be constructed using the following smart constructors:

Note: All smart constructors (and ArrayFire internally) assume column-major order.

>>> scalar @Double 2.0
ArrayFire Array
[1 1 1 1]
   2.0000
>>> vector @Double 10 [1..]
ArrayFire Array
[10 1 1 1]
    1.0000
    2.0000
    3.0000
    4.0000
    5.0000
    6.0000
    7.0000
    8.0000
    9.0000
   10.0000
>>> matrix @Double (2,2) [[1,2],[3,4]]
ArrayFire Array
[2 2 1 1]
    1.0000     3.0000
    2.0000     4.0000
>>> cube @Double (2,2,2) [[[2,2],[2,2]],[[2,2],[2,2]]]
ArrayFire Array
[2 2 2 1]
   2.0000     2.0000
   2.0000     2.0000

   2.0000     2.0000
   2.0000     2.0000
>>> tensor @Double (2,2,2,2) [[[[2,2],[2,2]],[[2,2],[2,2]]], [[[2,2],[2,2]],[[2,2],[2,2]]]]
ArrayFire Array
[2 2 2 2]
    2.0000     2.0000
    2.0000     2.0000

    2.0000     2.0000
    2.0000     2.0000


    2.0000     2.0000
    2.0000     2.0000

    2.0000     2.0000
    2.0000     2.0000

Array construction can use Haskell's lazy lists, since take is called on each dimension before sending to the C API.

>>> mkArray @Double [5,3] [1..]
ArrayFire Array
[5 3 1 1]
   1.0000     6.0000    11.0000
   2.0000     7.0000    12.0000
   3.0000     8.0000    13.0000
   4.0000     9.0000    14.0000
   5.0000    10.0000    15.0000

Specifying up to 4 dimensions is allowed (anything higher is ignored).

Laws

Every Array has an instance of Eq, Num, Fractional, Floating and Show

Num

>>> 2.0 :: Array Double
ArrayFire Array
[1 1 1 1]
   2.0000
>>> scalar @Int 1 + scalar @Int 1
ArrayFire Array
[1 1 1 1]
        2
>>> scalar @Int 1 - scalar @Int 1
ArrayFire Array
[1 1 1 1]
        0
>>> scalar @Double 10 / scalar @Double 10
ArrayFire Array
[1 1 1 1]
  1.0000
>>> abs $ scalar @Double (-10)
ArrayFire Array
[1 1 1 1]
  10.0000
>>> negate (scalar @Double 10)
ArrayFire Array
[1 1 1 1]
  -10.0000
>>> fromInteger 1.0 :: Array Double
ArrayFire Array
[1 1 1 1]
   1.0000

Eq

>>> scalar @Double 1 [10] == scalar @Double 1 [10]
True
>>> scalar @Double 1 [10] /= scalar @Double 1 [10]
False

Floating

>>> pi :: Array Double
ArrayFire Array
[1 1 1 1]
   3.1416
>>> A.sqrt pi :: Array Double
ArrayFire Array
[1 1 1 1]
   1.7725

Fractional

>>> (pi :: Array Double) / pi
ArrayFire Array
[1 1 1 1]
   1.000
>>> recip 0.5 :: Array Double
ArrayFire Array
[1 1 1 1]
   2.000

Show

>>> 0.0 :: Array Double
ArrayFire Array
[1 1 1 1]
   0.000

Conversion

Any Array can be exported into Haskell using toVector. This will create a Storable vector suitable for use in other C programs.

>>> vector :: Vector Double <- toVector <$> randu @Double [10,10]

Serialization

Each Array can be serialized to disk and deserialized from disk efficiently.

import qualified ArrayFire as A
import           Control.Monad

main :: IO ()
main = do
  let arr = A.constant [1,1,1,1] 10
  idx <- A.saveArray "key" arr "file.array" False
  foundIndex <- A.readArrayKeyCheck "file.array" "key"
  when (idx == foundIndex) $ do
    array <- A.readArrayKey "file.array" "key"
    print array

-- ArrayFire Array
-- [ 1 1 1 1 ]
--         10

Device

The ArrayFire API is able to see which devices are present, and will by default use the GPU if available.

>>> afInfo
ArrayFire v3.6.4 (OpenCL, 64-bit Mac OSX, build 1b8030c5)
[0] APPLE: AMD Radeon Pro 555X Compute Engine, 4096 MB <-- brackets [] signify device being used.
-1- APPLE: Intel(R) UHD Graphics 630, 1536 MB

module Data.Int

module Data.Word