packages feed

lapack-0.2.4: test/Test/Permutation.hs

module Test.Permutation where

import qualified Test.Generator as Gen
import qualified Test.Utility as Util
import Test.Generator ((<|*|>))
import Test.Utility (equalArray, Tagged(Tagged))

import qualified Numeric.LAPACK.Permutation as Perm
import qualified Numeric.LAPACK.Matrix.Square as Square
import qualified Numeric.LAPACK.Matrix as Matrix
import qualified Numeric.LAPACK.Vector as Vector
import Numeric.LAPACK.Permutation
         (Permutation, Inversion(Inverted, NonInverted))
import Numeric.LAPACK.Matrix.Square (Square)
import Numeric.LAPACK.Matrix (ZeroInt, zeroInt, (<#>))
import Numeric.LAPACK.Vector (Vector)

import qualified Numeric.Netlib.Class as Class

import qualified Data.Array.Comfort.Storable as Array
import qualified Data.Array.Comfort.Shape as Shape

import Foreign.C.Types (CInt)

import Control.Monad (forM)
import Control.Applicative (liftA2, (<$>))

import Data.Monoid ((<>))

import qualified Test.QuickCheck as QC


genPivots :: [()] -> QC.Gen (Vector ZeroInt CInt)
genPivots nat = do
   let n = length nat
   let nc = fromIntegral n
   fmap (Vector.fromList (zeroInt n)) $
      forM (zip [1..] nat) $ \(i,()) -> QC.choose (i,nc)

genPerm :: [()] -> QC.Gen (Permutation ZeroInt)
genPerm = fmap (\p -> Perm.fromPivots NonInverted (Array.shape p) p) . genPivots


permutationPivots :: Inversion -> Vector ZeroInt CInt -> Bool
permutationPivots inv xs =
   Array.toList (Perm.toPivots inv (Perm.fromPivots inv (Array.shape xs) xs))
   ==
   Array.toList xs

determinantMultiply :: (Permutation ZeroInt, Permutation ZeroInt) -> Bool
determinantMultiply (p0,p1) =
   Perm.determinant (Perm.multiply p0 p1)
   ==
   Perm.determinant p0 <> Perm.determinant p1

transposeMultiply :: (Permutation ZeroInt, Permutation ZeroInt) -> Bool
transposeMultiply (p0,p1) =
   (Array.toList $ Perm.toPivots NonInverted $
    Perm.transpose (Perm.multiply p0 p1))
   ==
   (Array.toList $ Perm.toPivots NonInverted $
    Perm.multiply (Perm.transpose p1) (Perm.transpose p0))


genPermutation :: Gen.Matrix a Int Int (Permutation ZeroInt)
genPermutation =
   flip Gen.mapGen Gen.squareDim $ \_ sh@(Shape.ZeroBased n) ->
   let nc = fromIntegral n
   in fmap (Perm.fromPivots NonInverted sh . Vector.fromList sh) $
         forM [1..] $ \i -> QC.choose (i,nc)


permApply ::
   (Shape.C height, Eq height, Shape.C width, Class.Floating a) =>
   Inversion -> Permutation height ->
   Matrix.General height width a ->
   Matrix.General height width a
permApply inv = Perm.apply (inv==Inverted)

permToMatrix ::
   (Shape.C sh, Class.Floating a) =>
   Inversion -> Permutation sh -> Square sh a
permToMatrix inv =
   case inv of
      NonInverted -> Perm.toMatrix
      Inverted -> Matrix.transpose . Perm.toMatrix

applyToMatrix ::
   (Class.Floating a) =>
   Inversion -> (Permutation ZeroInt, Matrix.General ZeroInt ZeroInt a) -> Bool
applyToMatrix inv (p,m) =
   equalArray
      (permApply inv p m)
      (permToMatrix inv p <#> m)

applyMultiply ::
   (Class.Floating a) =>
   Inversion ->
   (Permutation ZeroInt, Permutation ZeroInt,
    Matrix.General ZeroInt ZeroInt a) -> Bool
applyMultiply inv (p0,p1,m) =
   equalArray
      (case inv of
         NonInverted -> permApply inv p0 $ permApply inv p1 m
         Inverted -> permApply inv p1 $ permApply inv p0 m)
      (permApply inv (Perm.multiply p0 p1) m)

applyTranspose ::
   (Class.Floating a) =>
   Inversion -> (Permutation ZeroInt, Matrix.General ZeroInt ZeroInt a) -> Bool
applyTranspose inv (p,m) =
   equalArray
      (permApply inv (Perm.transpose p) m)
      (Matrix.transpose (permToMatrix inv p) <#> m)


addTag :: Gen.T a dim array -> Gen.T a dim (Tagged a array)
addTag = fmap Tagged

taggedToMatrix ::
   (Class.Floating a) => Tagged a (Permutation ZeroInt) -> Square ZeroInt a
taggedToMatrix (Tagged p) = Perm.toMatrix p

determinantNumber ::
   (Class.Floating a, Eq a) => Tagged a (Permutation ZeroInt) -> Bool
determinantNumber tp@(Tagged p) =
   Perm.numberFromSign (Perm.determinant p)
   ==
   Square.determinant (taggedToMatrix tp)


tests :: [(String, QC.Property)]
tests =
   ("permutationPivots",
      QC.property $
         QC.forAll QC.arbitraryBoundedEnum $ \inv ->
         QC.forAll (QC.arbitrary >>= genPivots) $ \pivot ->
            permutationPivots inv pivot) :
   ("determinantMultiply",
      QC.property $
         QC.forAll
            (do nat <- QC.arbitrary
                liftA2 (,) (genPerm nat) (genPerm nat))
            determinantMultiply) :
   ("transposeMultiply",
      QC.property $
         QC.forAll
            (do nat <- QC.arbitrary
                liftA2 (,) (genPerm nat) (genPerm nat))
            transposeMultiply) :
   []



checkForAll ::
   (Show a, QC.Testable test) =>
   Gen.T tag dim a -> (a -> test) -> Tagged tag QC.Property
checkForAll gen = Util.checkForAll (Gen.run gen 10 5)

checkForAllExtra ::
   (Show a, Show b, QC.Testable test) =>
   QC.Gen a -> Gen.T tag dim b ->
   (a -> b -> test) -> Tagged tag QC.Property
checkForAllExtra = Gen.withExtra checkForAll

testsVar ::
   (Show a, Class.Floating a, Eq a) =>
   [(String, Tagged a QC.Property)]
testsVar =
   ("applyToMatrix",
      checkForAllExtra QC.arbitraryBoundedEnum
         ((,) <$> genPermutation <|*|> Gen.matrix) applyToMatrix) :
   ("applyMultiply",
      checkForAllExtra QC.arbitraryBoundedEnum
         ((,,) <$> genPermutation <|*|> genPermutation <|*|> Gen.matrix)
         applyMultiply) :
   ("applyTranspose",
      checkForAllExtra QC.arbitraryBoundedEnum
         ((,) <$> genPermutation <|*|> Gen.matrix) applyTranspose) :
   ("determinantNumber",
      checkForAll (addTag genPermutation) determinantNumber) :
   []