arrayfire-0.9.0.0: test/ArrayFire/ArraySpec.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module ArrayFire.ArraySpec where
import Control.Exception
import Data.Complex
import qualified Data.Vector.Storable as V
import Data.Word
import Foreign.C.Types
import GHC.Int
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck ((==>))
import ArrayFire hiding (not)
spec :: Spec
spec =
describe "Array tests" $ do
it "Should add two scalar arrays" $ do
(scalar @Int 1 + scalar @Int 1) `shouldBe` scalar @Int 2
it "Should create a 0 dimension array" $ do
getElements (mkArray @Int [3,0,1,1] []) `shouldBe` 0
it "Should create a 0 length array" $ do
getElements (mkArray @Int [0,0,0,1] []) `shouldBe` 0
it "Should create a 0 length array w/ 0 dimensions" $ do
getElements (mkArray @Int [0,0,0,0] []) `shouldBe` 0
it "Should create a column vector" $ do
let arr = mkArray @Int [9,1,1,1] (repeat 9)
isColumn arr `shouldBe` True
it "Should create a row vector" $ do
let arr = mkArray @Int [1,9,1,1] (repeat 9)
isRow arr `shouldBe` True
it "Should recognize a column array as a vector" $ do
let arr = mkArray @Int [9,1,1,1] (repeat 9)
isVector arr `shouldBe` True
it "Should recognize a row array as a vector" $ do
let arr = mkArray @Int [1,9,1,1] (repeat 9)
isVector arr `shouldBe` True
it "Should copy an array" $ do
let arr = mkArray @Int [9,9,1,1] (repeat 9)
let newArray = copyArray arr
newArray `shouldBe` arr
it "Should modify manual eval flag" $ do
setManualEvalFlag False
(`shouldBe` False) =<< getManualEvalFlag
it "Should return the number of elements" $ do
let arr = mkArray @Int [9,9,1,1] [1..]
getElements arr `shouldBe` 81
it "Should give an empty array" $ do
let arr = mkArray @Int [0,1,1,1] []
getElements arr `shouldBe` 0
isEmpty arr `shouldBe` True
it "Should create a scalar array" $ do
let arr = mkArray @Int [1] [1]
isScalar arr `shouldBe` True
it "Should get number of dims specified" $ do
let arr = mkArray @Int [1,1,1,1] [1]
getNumDims arr `shouldBe` 1
let arr = mkArray @Int [2,3,4,5] [1..]
getNumDims arr `shouldBe` 4
let arr = mkArray @Int [2,3,4] [1..]
getNumDims arr `shouldBe` 3
it "Should get value of dims specified" $ do
let arr = mkArray @Int [2,3,4,5] (repeat 1)
getDims arr `shouldBe` (2,3,4,5)
it "Should test Sparsity" $ do
let arr = mkArray @Double [2,2,1,1] (repeat 1)
isSparse arr `shouldBe` False
it "Should make a Bit array" $ do
let arr = mkArray @CBool [2,2] [1,1,1,1]
isBool arr `shouldBe` True
it "Should make an integer array" $ do
let arr = mkArray @Int [2,2] (repeat 1)
isInteger arr `shouldBe` True
it "Should make a Floating array" $ do
let arr = mkArray @Double [2,2] (repeat 1)
isFloating arr `shouldBe` True
let arr = mkArray @CBool [2,2] (repeat 1)
isFloating arr `shouldBe` False
it "Should make a Complex array" $ do
let arr = mkArray @(Complex Double) [2,2] (repeat 1)
isComplex arr `shouldBe` True
isReal arr `shouldBe` False
it "Should make a Real array" $ do
let arr = mkArray @Double [2,2] (repeat 1)
isReal arr `shouldBe` True
isComplex arr `shouldBe` False
it "Should make a Double precision array" $ do
let arr = mkArray @Double [2,2] (repeat 1)
isDouble arr `shouldBe` True
isSingle arr `shouldBe` False
it "Should make a Single precision array" $ do
let arr = mkArray @Float [2,2] (repeat 1)
isDouble arr `shouldBe` False
isSingle arr `shouldBe` True
it "Should make a Real floating array" $ do
let arr = mkArray @Float [2,2] (repeat 1)
isRealFloating arr `shouldBe` True
let arr = mkArray @Double [2,2] (repeat 1)
isRealFloating arr `shouldBe` True
it "Should get reference count" $ do
let arr1 = mkArray @Float [2,2] (repeat 1)
arr2 = retainArray arr1
arr3 = retainArray arr2
getDataRefCount arr3 `shouldBe` 3
it "Should convert an array to a list" $ do
let arr = mkArray @Double [30,30] (repeat 1)
toList arr `shouldBe` Prelude.replicate (30 * 30) 1
let arr = mkArray @Float [10,10] (repeat (5.5))
toList arr `shouldBe` Prelude.replicate 100 5.5
let arr = mkArray @CBool [4] [1,1,0,1]
toList arr `shouldBe` [1,1,0,1]
let arr = mkArray @Int16 [10] [1..]
toList arr `shouldBe` [1..10]
let arr = mkArray @Int32 [100] [1..100]
toList arr `shouldBe` [1..100]
let arr = mkArray @Int64 [100] [1..100]
toList arr `shouldBe` [1..100]
let arr = mkArray @Int [100] [1..100]
toList arr `shouldBe` [1..100]
let arr = mkArray @(Complex Float) [1] [1 :+ 1]
toList arr `shouldBe` [1 :+ 1]
let arr = mkArray @(Complex Double) [1] [1 :+ 1]
toList arr `shouldBe` [1 :+ 1]
let arr = mkArray @Word16 [10] [1..10]
toList arr `shouldBe` [1..10]
let arr = mkArray @Word32 [10] [1..10]
toList arr `shouldBe` [1..10]
let arr = mkArray @Word64 [10] [1..10]
toList arr `shouldBe` [1..10]
let arr = mkArray @Word [10] [1..10]
toList arr `shouldBe` [1..10]
-- Regression: toVector previously allocated len*size bytes instead of size,
-- causing quadratic memory use. These round-trips verify correct element count
-- and values at sizes where the bug was most wasteful.
describe "toVector round-trip" $ do
it "preserves all elements for a 1000-element Double array" $ do
let xs = [1..1000] :: [Double]
arr = mkArray @Double [1000] xs
V.toList (toVector arr) `shouldBe` xs
it "preserves all elements for a 500-element Int array" $ do
let xs = [1..500] :: [Int]
arr = mkArray @Int [500] xs
V.toList (toVector arr) `shouldBe` xs
it "length of toVector matches getElements" $ do
let arr = mkArray @Double [7, 13] (repeat 0)
V.length (toVector arr) `shouldBe` getElements arr
describe "fromVector" $ do
it "round-trips a Double vector" $ do
let xs = V.fromList [1..10 :: Double]
arr = fromVector @Double [10] xs
toVector arr `shouldBe` xs
it "round-trips an Int vector" $ do
let xs = V.fromList [1..100 :: Int]
arr = fromVector @Int [100] xs
toVector arr `shouldBe` xs
it "round-trips a Complex Double vector" $ do
let xs = V.fromList [1 :+ 2, 3 :+ 4 :: Complex Double]
arr = fromVector @(Complex Double) [2] xs
toVector arr `shouldBe` xs
it "produces the same result as mkArray" $ do
let xs = [1..25 :: Double]
arr1 = mkArray @Double [5,5] xs
arr2 = fromVector @Double [5,5] (V.fromList xs)
arr2 `shouldBe` arr1
it "throws on dimension mismatch" $ do
let xs = V.fromList [1,2,3 :: Double]
evaluate (fromVector @Double [4] xs) `shouldThrow` anyException
-- Round-trip is data-preserving (no arithmetic), so equality is exact.
-- This also guards the toVector allocation fix against host over-reads.
prop "toVector . fromVector == id (Double)" $ \(xs :: [Double]) ->
not (null xs) ==>
let v = V.fromList xs
in V.toList (toVector (fromVector @Double [length xs] v)) == xs
prop "toVector . fromVector == id (Int)" $ \(xs :: [Int]) ->
not (null xs) ==>
let v = V.fromList xs
in V.toList (toVector (fromVector @Int [length xs] v)) == xs
describe "cube" $ do
it "creates a 2x2x2 cube with correct dims" $ do
let c = cube @Double (2,2,2)
[ [[1,2],[3,4]], [[5,6],[7,8]] ]
getDims c `shouldBe` (2,2,2,1)
it "creates a 2x2x2 cube with correct element count" $ do
let c = cube @Double (2,2,2)
[ [[1,2],[3,4]], [[5,6],[7,8]] ]
getElements c `shouldBe` 8
it "all-constant cube equals constant array" $ do
let c = cube @Double (2,2,2)
[ [[3,3],[3,3]], [[3,3],[3,3]] ]
c `shouldBe` mkArray @Double [2,2,2] (replicate 8 3)
describe "tensor" $ do
it "creates a 2x2x2x2 tensor with correct dims" $ do
let t = tensor @Double (2,2,2,2)
[ [ [[1,2],[3,4]], [[5,6],[7,8]] ]
, [ [[1,2],[3,4]], [[5,6],[7,8]] ]
]
getDims t `shouldBe` (2,2,2,2)
it "creates a 2x2x2x2 tensor with correct element count" $ do
let t = tensor @Double (2,2,2,2)
[ [ [[1,2],[3,4]], [[5,6],[7,8]] ]
, [ [[1,2],[3,4]], [[5,6],[7,8]] ]
]
getElements t `shouldBe` 16
it "all-constant tensor equals constant array" $ do
let t = tensor @Double (2,2,2,2)
[ [ [[5,5],[5,5]], [[5,5],[5,5]] ]
, [ [[5,5],[5,5]], [[5,5],[5,5]] ]
]
t `shouldBe` mkArray @Double [2,2,2,2] (replicate 16 5)