hasktorch-0.0.1.0: tests/Torch/Core/RandomSpec.hs
{-# LANGUAGE ScopedTypeVariables #-}
module Torch.Core.RandomSpec (spec) where
import Test.Hspec
import Test.QuickCheck
import Test.QuickCheck.Monadic
import Control.Monad (replicateM)
import Foreign (Ptr)
import qualified Control.Exception as E
import Torch.Core.Random as R
import Torch.Prelude.Extras (doesn'tCrash)
import Orphans ()
main :: IO ()
main = hspec spec
spec :: Spec
spec = do
describe "newRNG" newRNGSpec
describe "seed" seedSpec
describe "manualSeed" manualSeedSpec
describe "initialSeed" initialSeedSpec
describe "random" randomSpec
describe "uniform" uniformSpec
describe "normal" normalSpec
describe "exponential" exponentialSpec
describe "cauchy" cauchySpec
describe "logNormal" logNormalSpec
describe "geometric" geometricSpec
describe "bernoulli" bernoulliSpec
describe "scenario" $ do
it "runs this scenario as expected" $ testScenario
newRNGSpec :: Spec
newRNGSpec = do
rngs <- runIO (replicateM 10 R.newRNG)
it "always creates a new random number" $
zipWith (==) (tail rngs) (init rngs) `shouldNotContain` [True]
seedSpec :: Spec
seedSpec = do
beforeAll
(do
rngs <- (replicateM 10 R.newRNG)
rng1 <- mapM seed rngs
rng2 <- mapM seed rngs
pure (rngs, rng1, rng2)
)
(describe "seedSpec" $ do
it "generates different values, given the same starting generators" $
\(rngs, rng1, rng2) -> do
zipWith (==) rng1 rng2 `shouldNotContain` [True]
)
manualSeedSpec :: Spec
manualSeedSpec = do
rngs <- runIO (replicateM 10 R.newRNG)
rng1 <- runIO $ mapM (`manualSeed` 1) rngs
rng2 <- runIO $ mapM (`manualSeed` 1) rngs
it "generates the same value, given the same seed values" $
zipWith (==) rng1 rng2 `shouldNotContain` [False]
initialSeedSpec :: Spec
initialSeedSpec = do
it "doesn't crash" $
pending
randomSpec :: Spec
randomSpec = do
rngs <- runIO (replicateM 10 R.newRNG)
rs <- runIO $ mapM random rngs
it "generates numbers and doesn't crash" $
rs `shouldSatisfy` doesn'tCrash
uniformSpec :: Spec
uniformSpec = do
rng <- runIO R.newRNG
distributed2BoundsCheck rng uniform $ \a b x ->
case compare a b of
LT -> x <= b && x >= a
_ -> x <= a && x >= b
normalSpec :: Spec
normalSpec = do
rng <- runIO R.newRNG
distributed2BoundsCheck rng (withStdv normal) (\a b x -> doesn'tCrash ())
exponentialSpec :: Spec
exponentialSpec = do
rng <- runIO R.newRNG
distributed1BoundsCheck rng exponential property (\a x -> doesn'tCrash ())
cauchySpec :: Spec
cauchySpec = do
rng <- runIO R.newRNG
distributed2BoundsCheck rng cauchy (\a b x -> doesn'tCrash ())
logNormalSpec :: Spec
logNormalSpec = do
rng <- runIO R.newRNG
distributed2BoundsCheck rng (withStdv logNormal) (\a b x -> doesn'tCrash ())
geometricSpec :: Spec
geometricSpec = do
rng <- runIO R.newRNG
distributed1BoundsCheck rng geometric (forAll $ choose (0.0001, 0.9999)) (\a x -> doesn'tCrash ())
bernoulliSpec :: Spec
bernoulliSpec = do
rng <- runIO R.newRNG
distributed1BoundsCheck rng bernoulli (forAll $ choose (0.0001, 0.9999)) (\a x -> doesn'tCrash ())
-- |Check that seeds work as intended
testScenario :: IO ()
testScenario = do
rng <- R.newRNG
manualSeed rng 332323401
val1 <- normal rng 0.0 1000
val2 <- normal rng 0.0 1000
E.assert (val1 /= val2) pure ()
manualSeed rng 332323401
manualSeed rng 332323401
val3 <- normal rng 0.0 1000.0
E.assert (val1 == val3) pure ()
-- ========================================================================= --
withStdv
:: (Generator -> a -> b -> IO Double)
-> Generator
-> a
-> NonZero (Positive b)
-> IO Double
withStdv fn g a b = fn g a (getPositive (getNonZero b))
distributed2BoundsCheck
:: (Show a, Show b, Arbitrary a, Arbitrary b)
=> Generator
-> (Generator -> a -> b -> IO Double)
-> (a -> b -> Double -> Bool)
-> Spec
distributed2BoundsCheck g fun check = do
it "should generate random numbers in the correct bounds" . property $ \(a, b) ->
monadicIO $ do
x <- run (fun g a b)
assert (check a b x)
distributed1BoundsCheck :: (Show a, Arbitrary a) => Generator -> (Generator -> a -> IO b) -> ((a -> Property) -> Property) -> (a -> b -> Bool) -> Spec
distributed1BoundsCheck g fun pfun check = do
it "should generate random numbers in the correct bounds" . pfun $ \a -> monadicIO $ do
x <- run (fun g a)
assert (check a x)