packages feed

easytensor-2.1.0.0: test/Numeric/Subroutine/SolveTriangularTest.hs

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE PolyKinds           #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell     #-}
{-# LANGUAGE TypeApplications    #-}
{-# LANGUAGE TypeOperators       #-}

module Numeric.Subroutine.SolveTriangularTest (runTests) where


import Control.Monad.ST
import Numeric.Arbitraries
import Numeric.DataFrame
import Numeric.DataFrame.ST
import Numeric.Dimensions
import Numeric.Subroutine.SolveTriangular
import Test.QuickCheck


arbitraryTriangular ::
       forall (n :: Nat) (m :: Nat)
     . (KnownDim n, KnownDim m, KnownBackend Double '[m])
    => Bool -> Gen (DataFrame Double '[n,m])
arbitraryTriangular upper = iwmap f <$> arbitrary
  where
    f :: Idxs '[n] -> DataFrame Double '[m] -> DataFrame Double '[m]
    f (Idx i :* U) x = iwmap g x
      where
        a = maxElem x
        g :: Idxs '[m] -> Scalar Double -> Scalar Double
        g (Idx j :* U) c
          | if upper then j < i else j > i = 0
          | j == i = if c >= 0 then c + 1 + a else c - 1 - a
          | otherwise = c

-- | Test Rx = b
testSolveUpperTriangularR ::
       forall (n :: Nat) (m :: Nat) (ds :: [Nat])
     . (KnownDim n, KnownDim m, m <= n, Dimensions ds)
    => DataFrame Double '[n,m]     -- ^ R
    -> DataFrame Double  (n :+ ds) -- ^ b
    -> Property
testSolveUpperTriangularR r b
  | dn <- dim @n
  , dm <- dim @m
  , di@D <- dn `minusDim` dm `plusDim` D1
  , i0 <- (Idx 0 :* U) `inSpaceOf` (di :* U)
  , Just Dict <- sameDim (plusDim dn D1) (plusDim di dm)
    = let bm :: DataFrame Double (m :+ ds)
          bm = slice i0 b
          x :: DataFrame Double (m :+ ds)
          x = runST $ do
            xPtr <- thawDataFrame bm
            solveUpperTriangularR r xPtr
            unsafeFreezeDataFrame xPtr
      in counterexample
          (unlines
            [ "failed Rx = b:"
            , "R: " ++ show r
            , "x: " ++ show x
            , "b: " ++ show b
            , "Rx:" ++ show (r %* x)
            ]
          ) (approxEq (maxElem r) b (r %* x))
testSolveUpperTriangularR _ _ = error "impossible pattern"

prop_SolveUpperTriangularR :: Property
prop_SolveUpperTriangularR = property run
  where
    run :: Gen Property
    run = do
      Dx (m@D :: Dim m) <- arbitrary :: Gen (Dim (XN 1))
      Dx (D   :: Dim n) <- arbitrary :: Gen (Dim (XN m))
      justVec           <- arbitrary :: Gen Bool
      SomeDims (Dims :: Dims ds)
                      <- if justVec
                         then pure (SomeDims U)
                         else removeDimsAbove 100 <$> arbitrary
      r <- arbitraryTriangular @n @m True
      b' <- arbitrary @(DataFrame Double (n :+ ds))
      let b = iwmap @_ @(n ': ds) @'[]
                (\(Idx i :* _) x -> if i >= dimVal m then 0 else x) b'
      return $ testSolveUpperTriangularR r b

-- | Test xR = b
testSolveUpperTriangularL ::
       forall (n :: Nat) (m :: Nat) (ds :: [Nat])
     . (KnownDim n, KnownDim m, m <= n, Dimensions ds)
    => DataFrame Double  (ds +: m) -- ^ b
    -> DataFrame Double '[n,m] -- ^ r
    -> Property
testSolveUpperTriangularL b r
  | dn <- dim @n
  , dm <- dim @m
  , dnm@D  <- minusDim dn dm
  , Dims <- Snoc (dims @ds) dn
  , Dims <- Snoc (dims @ds) dm
  , Dict <- Dict @(SnocList ds n _)
  , Dict <- Dict @(SnocList ds m _)
  , Just Dict <- sameDim dn (plusDim dm dnm)
    = let padZeroes :: DataFrame Double (ds +: m) -> DataFrame Double (ds +: n)
          padZeroes = ewmap @_ @ds @'[n] $ \a -> appendDF a (0 `inSpaceOf` (dnm :* U))
          x :: DataFrame Double (ds +: n)
          x = runST $ do
            xPtr <- thawDataFrame b
            solveUpperTriangularL xPtr r
            padZeroes <$> unsafeFreezeDataFrame xPtr
      in  counterexample
            (unlines
              [ "failed xR = b:"
              , "R: " ++ show r
              , "x: " ++ show x
              , "b: " ++ show b
              , "xR:" ++ show (x %* r)
              ]
            ) (approxEq (maxElem r) b (x %* r))
testSolveUpperTriangularL _ _ = error "impossible pattern"

prop_SolveUpperTriangularL :: Property
prop_SolveUpperTriangularL = property run
  where
    run :: Gen Property
    run = do
      Dx (m@D :: Dim m) <- arbitrary :: Gen (Dim (XN 1))
      Dx (D   :: Dim n) <- arbitrary :: Gen (Dim (XN m))
      justVec           <- arbitrary :: Gen Bool
      SomeDims (ds@Dims :: Dims ds)
                      <- if justVec
                         then pure (SomeDims U)
                         else removeDimsAbove 100 <$> arbitrary
      Dims <- pure $ Snoc ds m
      r <- arbitraryTriangular @n @m True
      b <- arbitrary @(DataFrame Double (ds +: m))
      return $ testSolveUpperTriangularL b r


-- | Test Lx = b
testSolveLowerTriangularR ::
       forall (n :: Nat) (m :: Nat) (ds :: [Nat])
     . (KnownDim n, KnownDim m, n <= m, Dimensions ds)
    => DataFrame Double '[n,m]     -- ^ L
    -> DataFrame Double  (n :+ ds) -- ^ b
    -> Property
testSolveLowerTriangularR l b
  | dn <- dim @n
  , dm <- dim @m
  , dmn@D <- dm `minusDim` dn
  , Just Dict <- sameDim dm (dn `plusDim` dmn)
    = let padZeroes :: DataFrame Double (n :+ ds) -> DataFrame Double (m :+ ds)
          padZeroes z = appendDF z (0 :: DataFrame Double ((m-n) :+ ds))
          x :: DataFrame Double (m :+ ds)
          x = runST $ do
            xPtr <- thawDataFrame b
            solveLowerTriangularR l xPtr
            padZeroes <$> unsafeFreezeDataFrame xPtr
      in counterexample
          (unlines
            [ "failed Lx = b:"
            , "L: " ++ show l
            , "x: " ++ show x
            , "b: " ++ show b
            , "Lx:" ++ show (l %* x)
            ]
          ) (approxEq (maxElem l) b (l %* x))
testSolveLowerTriangularR _ _ = error "impossible pattern"

prop_SolveLowerTriangularR :: Property
prop_SolveLowerTriangularR = property run
  where
    run :: Gen Property
    run = do
      Dx (D :: Dim n) <- arbitrary :: Gen (Dim (XN 1))
      Dx (D :: Dim m) <- arbitrary :: Gen (Dim (XN n))
      justVec         <- arbitrary :: Gen Bool
      SomeDims (Dims :: Dims ds)
                      <- if justVec
                         then pure (SomeDims U)
                         else removeDimsAbove 100 <$> arbitrary
      l <- arbitraryTriangular @n @m False
      b <- arbitrary @(DataFrame Double (n :+ ds))
      return $ testSolveLowerTriangularR l b


-- | Test xL = b
testSolveLowerTriangularL ::
       forall (n :: Nat) (m :: Nat) (ds :: [Nat])
     . (KnownDim n, KnownDim m, n <= m, Dimensions ds)
    => DataFrame Double  (ds +: m) -- ^ b
    -> DataFrame Double '[n,m] -- ^ L
    -> Property
testSolveLowerTriangularL b l
  | dn <- dim @n
  , dm <- dim @m
  , dmn@D  <- minusDim dm dn
  , Dims <- Snoc (dims @ds) dn
  , Dims <- Snoc (dims @ds) dm
  , Dict <- Dict @(SnocList ds n _)
  , Dict <- Dict @(SnocList ds m _)
  , Just Dict <- sameDim dm (plusDim dn dmn)
  , di@D <- dm `minusDim` dn `plusDim` D1
  , i0 <- (Idx 0 :* U) `inSpaceOf` (di :* U)
  , Just Dict <- sameDim (plusDim dm D1) (plusDim di dn)
    = let dropLast :: DataFrame Double (ds +: m) -> DataFrame Double (ds +: n)
          dropLast = ewmap @_ @ds @'[n] $ slice i0
          x :: DataFrame Double (ds +: n)
          x = runST $ do
            xPtr <- thawDataFrame b
            solveLowerTriangularL xPtr l
            dropLast <$> unsafeFreezeDataFrame xPtr
      in  counterexample
            (unlines
              [ "failed xL = b:"
              , "R: " ++ show l
              , "x: " ++ show x
              , "b: " ++ show b
              , "xL:" ++ show (x %* l)
              ]
            ) (approxEq (maxElem l) b (x %* l))
testSolveLowerTriangularL _ _ = error "impossible pattern"

prop_SolveLowerTriangularL :: Property
prop_SolveLowerTriangularL = property run
  where
    run :: Gen Property
    run = do
      Dx (n@D :: Dim n) <- arbitrary :: Gen (Dim (XN 1))
      Dx (m@D :: Dim m) <- arbitrary :: Gen (Dim (XN n))
      justVec           <- arbitrary :: Gen Bool
      SomeDims (ds@Dims :: Dims ds)
                      <- if justVec
                         then pure (SomeDims U)
                         else removeDimsAbove 100 <$> arbitrary
      Dims <- pure $ Snoc ds m
      Dict <- pure $ Dict @(SnocList ds m _)
      l <- arbitraryTriangular @n @m False
      b' <- arbitrary @(DataFrame Double (ds +: m))
      let b = ewmap @_ @ds @'[m]
                (iwmap @_ @'[m] @'[]
                  (\(Idx i :* _) x -> if i >= dimVal n then 0 else x)
                ) b'
      return $ testSolveLowerTriangularL b l

return []
runTests :: Int -> IO Bool
runTests n = $forAllProperties
  $ quickCheckWithResult stdArgs { maxSuccess = n }