packages feed

arrayfire-0.9.0.0: test/ArrayFire/DataSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
module ArrayFire.DataSpec where

import           Control.Exception
import           Data.Bits             (complement)
import           Data.Complex
import           Data.Word
import           Foreign.C.Types
import           GHC.Int
import           Prelude hiding (flip)
import           Test.Hspec
import           Test.Hspec.QuickCheck (prop)
import           Test.QuickCheck       ((==>))

import           ArrayFire hiding (not)

spec :: Spec
spec =
  describe "Data tests" $ do
    it "Should create constant Array" $ do
      constant @Float [1] 1 `shouldBe` 1
      constant @Double [1] 1 `shouldBe` 1
      constant @Int16 [1] 1 `shouldBe` 1
      constant @Int32 [1] 1 `shouldBe` 1
      constant @Int64 [1] 1 `shouldBe` 1
      constant @Int [1] 1 `shouldBe` 1
      constant @Word16 [1] 1 `shouldBe` 1
      constant @Word32 [1] 1 `shouldBe` 1
      constant @Word64 [1] 1 `shouldBe` 1
      constant @Word [1] 1 `shouldBe` 1
      constant @CBool [1] 1 `shouldBe` 1
      constant @(Complex Double) [1] (1.0 :+ 1.0)
        `shouldBe`
          constant @(Complex Double) [1] (1.0 :+ 1.0)
      constant @(Complex Float) [1] (1.0 :+ 1.0)
        `shouldBe`
          constant @(Complex Float) [1] (1.0 :+ 1.0)

    describe "arange" $ do
      it "generates a sequence along dim 0 for a 1D array" $ do
        arange @Double [5] (-1) `shouldBe` vector @Double 5 [0,1,2,3,4]
      it "generates a sequence along dim 1 for a 2D array" $ do
        arange @Double [3,2] 1 `shouldBe` mkArray @Double [3,2] [0,0,0,1,1,1]

    describe "iota" $ do
      it "generates a flat sequence without tiling" $ do
        iota @Double [5] [] `shouldBe` vector @Double 5 [0,1,2,3,4]
      it "tiles the sequence along dim 0" $ do
        iota @Double [3] [2] `shouldBe` vector @Double 6 [0,1,2,0,1,2]

    describe "identity" $ do
      it "creates a 2x2 identity matrix" $ do
        identity @Double [2,2]
          `shouldBe` mkArray @Double [2,2] [1,0,0,1]
      it "creates a 3x3 identity matrix" $ do
        identity @Double [3,3]
          `shouldBe` mkArray @Double [3,3] [1,0,0,0,1,0,0,0,1]

    describe "diagCreate" $ do
      it "creates a diagonal matrix from a vector (diag 0)" $ do
        diagCreate (vector @Double 3 [1,2,3]) 0
          `shouldBe` mkArray @Double [3,3] [1,0,0,0,2,0,0,0,3]
      it "creates a superdiagonal matrix (diag 1)" $ do
        diagCreate (vector @Double 2 [5,6]) 1
          `shouldBe` mkArray @Double [3,3] [0,0,0,5,0,0,0,6,0]

    describe "diagExtract" $ do
      it "extracts the main diagonal of a square matrix" $ do
        diagExtract (mkArray @Double [3,3] [1,0,0,0,2,0,0,0,3]) 0
          `shouldBe` vector @Double 3 [1,2,3]
      it "is the inverse of diagCreate on the main diagonal" $ do
        let v = vector @Double 4 [1,2,3,4]
        diagExtract (diagCreate v 0) 0 `shouldBe` v

    describe "lower" $ do
      it "extracts the lower triangular part (unit diagonal)" $ do
        let m = mkArray @Double [3,3] [1,2,3,4,5,6,7,8,9]
        lower m True
          `shouldBe` mkArray @Double [3,3] [1,2,3,0,1,6,0,0,1]
      it "extracts the lower triangular part (non-unit diagonal)" $ do
        let m = mkArray @Double [3,3] [1,2,3,4,5,6,7,8,9]
        lower m False
          `shouldBe` mkArray @Double [3,3] [1,2,3,0,5,6,0,0,9]

    describe "upper" $ do
      it "extracts the upper triangular part (unit diagonal)" $ do
        let m = mkArray @Double [3,3] [1,2,3,4,5,6,7,8,9]
        upper m True
          `shouldBe` mkArray @Double [3,3] [1,0,0,4,1,0,7,8,1]
      it "extracts the upper triangular part (non-unit diagonal)" $ do
        let m = mkArray @Double [3,3] [1,2,3,4,5,6,7,8,9]
        upper m False
          `shouldBe` mkArray @Double [3,3] [1,0,0,4,5,0,7,8,9]

    describe "tile" $ do
      it "tiles a scalar into a 3x3 array" $ do
        tile (scalar @Int 7) [3,3]
          `shouldBe` constant @Int [3,3] 7
      it "tiles a row vector along dim 0" $ do
        tile (mkArray @Int [1,3] [1,2,3]) [2,1]
          `shouldBe` mkArray @Int [2,3] [1,1,2,2,3,3]

    describe "moddims" $ do
      it "reshapes a vector into a matrix" $ do
        moddims (vector @Int 6 [1..6]) [2,3]
          `shouldBe` mkArray @Int [2,3] [1,2,3,4,5,6]
      it "reshapes a matrix back to a vector" $ do
        let v = vector @Int 6 [1..6]
        moddims (moddims v [2,3]) [6] `shouldBe` v

    describe "flat" $ do
      it "flattens a 2x3 matrix to a 6-element vector" $ do
        flat (mkArray @Int [2,3] [1,2,3,4,5,6])
          `shouldBe` vector @Int 6 [1,2,3,4,5,6]

    describe "flip" $ do
      it "reverses a vector (dim 0)" $ do
        flip (vector @Int 4 [1,2,3,4]) 0
          `shouldBe` vector @Int 4 [4,3,2,1]
      it "reverses columns of a matrix (dim 1)" $ do
        flip (mkArray @Int [2,2] [1,2,3,4]) 1
          `shouldBe` mkArray @Int [2,2] [3,4,1,2]

    describe "shift" $ do
      it "shifts a vector by 2 elements (wrapping)" $ do
        shift (vector @Double 4 [1,2,3,4]) 2 0 0 0
          `shouldBe` vector @Double 4 [3,4,1,2]

    describe "select" $ do
      it "selects elements from two arrays based on a boolean mask" $ do
        let cond = vector @CBool 4 [1,0,1,0]
            a    = vector @Double 4 [10,20,30,40]
            b    = vector @Double 4 [1,2,3,4]
        select cond a b `shouldBe` vector @Double 4 [10,2,30,4]

    describe "selectScalarR" $ do
      it "uses scalar for false positions" $ do
        let cond = vector @CBool 4 [1,0,1,0]
            a    = vector @Double 4 [10,20,30,40]
        selectScalarR cond a 99 `shouldBe` vector @Double 4 [10,99,30,99]

    describe "selectScalarL" $ do
      it "uses scalar for true positions" $ do
        let cond = vector @CBool 4 [1,0,1,0]
            b    = vector @Double 4 [1,2,3,4]
        selectScalarL cond 99 b `shouldBe` vector @Double 4 [99,2,99,4]

    it "Should join Arrays along the specified dimension" $ do
      join 0 (constant @Int [1, 3] 1) (constant @Int [1, 3] 2) `shouldBe` mkArray @Int [2, 3] [1, 2, 1, 2, 1, 2]
      join 1 (constant @Int [1, 2] 1) (constant @Int [1, 2] 2) `shouldBe` mkArray @Int [1, 4] [1, 1, 2, 2]
      joinMany 0 [constant @Int [1, 3] 1, constant @Int [1, 3] 2] `shouldBe` mkArray @Int [2, 3] [1, 2, 1, 2, 1, 2]
      joinMany 1 [constant @Int [1, 2] 1, constant @Int [1, 1] 2, constant @Int [1, 3] 3] `shouldBe` mkArray @Int [1, 6] [1, 1, 2, 3, 3, 3]

    describe "bitNot" $ do
      it "complements 0 to all-ones (-1 in two's complement) for Int32" $ do
        bitNot (scalar @Int32 0) `shouldBe` scalar @Int32 (-1)
      it "complements -1 to 0 for Int32" $ do
        bitNot (scalar @Int32 (-1)) `shouldBe` scalar @Int32 0
      it "complements 0 to maxBound for Word32" $ do
        bitNot (scalar @Word32 0) `shouldBe` scalar @Word32 maxBound
      it "bitNot . bitNot == id" $ do
        let v = vector @Int32 4 [0, 1, -1, 42]
        bitNot (bitNot v) `shouldBe` v
      prop "bitNot is an involution (Int32)" $ \(xs :: [Int32]) ->
        not (null xs) ==>
          toList (bitNot (bitNot (vector @Int32 (length xs) xs))) == xs
      prop "bitNot agrees with Data.Bits.complement (Int32)" $ \(xs :: [Int32]) ->
        not (null xs) ==>
          toList (bitNot (vector @Int32 (length xs) xs)) == map complement xs

    describe "reorder" $ do
      it "reorder [0,1] is identity for a 2D matrix" $ do
        let m = matrix @Double (3,4) [[1..3],[3..6],[6..9],[9..12]]
        reorder m [0,1] `shouldBe` m
      it "reorder [1,0] transposes a matrix" $ do
        let m = matrix @Double (2,3) [[1,2],[3,4],[5,6]]
        getDims (reorder m [1,0]) `shouldBe` (3,2,1,1)
      it "reorder [1,0] then [1,0] round-trips" $ do
        let m = matrix @Double (3,4) [[1..3],[3..6],[6..9],[9..12]]
        reorder (reorder m [1,0]) [1,0] `shouldBe` m