packages feed

xorshift-plus (empty) → 0.1.0.0

raw patch · 10 files changed

+415/−0 lines, 10 filesdep +QuickCheckdep +Xorshift128Plusdep +basesetup-changed

Dependencies added: QuickCheck, Xorshift128Plus, base, doctest, gauge, ghc-prim, hspec, random, xorshift, xorshift-plus

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for xorshift-plus++## 0.1.0.0 -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2019, OSANAI Kazuyoshi++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of OSANAI Kazuyoshi nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,23 @@+# xorshift-plus++Simple implementation of xorshift+.++```haskell+import Random.XorshiftPlus+main = do+  s <- genXorshiftPlusInt 1+  i <- getInt s+  print i -- 2455688531189531812+```++## Performance++```+> cabal new-run --enable-benchmarks micro -- compareWithOtherPRNGs --small+Up to date+compareWithOtherPRNGs/xorshift-plus_Int (THIS PACKAGE) mean 10.63 μs  ( +- 40.63 ns  )+compareWithOtherPRNGs/xorshift-plus_Word (THIS PACKAGE) mean 10.72 μs  ( +- 36.54 ns  )+compareWithOtherPRNGs/xorshift_Int32     mean 250.5 μs  ( +- 917.1 ns  )+compareWithOtherPRNGs/xorshift_Ina64     mean 457.1 μs  ( +- 1.070 μs  )+compareWithOtherPRNGs/Xorshift128Plus_Word64 mean 24.61 μs  ( +- 111.9 ns  )+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ benchmarks/micro/micro.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE TypeApplications #-}++module Main where++import Gauge.Main+import Data.Int+import Data.Word+import System.Random+import Random.XorshiftPlus+import Random.Xorshift.Int32+import Random.Xorshift.Int64+import qualified System.Random.Xorshift128Plus as X128+import System.CPUTime++size :: Int+size = 1000++getSeed :: Num a => IO a+getSeed = fromInteger `fmap` getCPUTime++getRandomIntList :: Int -> IO [Int]+getRandomIntList n = do+  seed <- getSeed @ Int+  xorshift <- genXorshiftPlusInt seed+  sequence $ replicate n $ getInt xorshift++getRandomWordList :: Int -> IO [Word]+getRandomWordList n = do+  seed <- getSeed @ Word+  xorshift <- genXorshiftPlusWord seed+  sequence $ replicate n $ getWord xorshift++getRandomIntList_xorshift_Int32 :: Int -> IO [Int32]+getRandomIntList_xorshift_Int32 n = do+  seed <- getSeed @ Int32+  let xorshift = makeXorshift32 seed+  return $ take n $ randoms xorshift++getRandomIntList_xorshift_Int64 :: Int -> IO [Int64]+getRandomIntList_xorshift_Int64 n = do+  seed <- getSeed @ Int64+  let xorshift = makeXorshift64 seed+  return $ take n $ randoms xorshift++getRandomWord64List_Xorshift128Plus :: Int -> IO [Word64]+getRandomWord64List_Xorshift128Plus n = do+  seed <- getSeed @ Word64+  let xorshift = X128.initialize seed+  return $ take n $ makeList xorshift+  where+    makeList gen = let (x, gen') = X128.next gen in x : (makeList gen')++main :: IO ()+main = defaultMain+  [ bgroup "compareWithOtherPRNGs"+    [ bench "xorshift-plus_Int (THIS PACKAGE)" $ nfIO (getRandomIntList size)+    , bench "xorshift-plus_Word (THIS PACKAGE)" $ nfIO (getRandomWordList size)+    , bench "xorshift_Int32" $ nfIO (getRandomIntList_xorshift_Int32 size)+    , bench "xorshift_Ina64" $ nfIO (getRandomIntList_xorshift_Int64 size)+    , bench "Xorshift128Plus_Word64" $ nfIO (getRandomWord64List_Xorshift128Plus size)+    ]+  ]
+ src/Random/XorshiftPlus.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE StrictData #-}++{-|+Module      : Random.XorshiftPlus+Description : xorshift+ implementation+Copyright   : (c) OSANAI Kazuyoshi, 2019+License     : BSD 3-Clause+Maintainer  : osmium.k@gmail.com+Stability   : experimental+Portability : GHC, word size 64bit++Simple implementation of xorshift+.++>>> gen <- genXorshiftPlusInt 1+>>> getInt gen+2455688531189531812+-}+module Random.XorshiftPlus+  (+  -- * IO version+    XorshiftPlus+  -- ** Generator+  , genXorshiftPlusWord+  , genXorshiftPlusInt+  -- ** Values+  , getWord+  , getInt+  , getDouble+  -- * Low level, ST version+  , XorshiftPlusST+  , genXorshiftPlusWordST+  , getWordST+  -- * Internal+  , splitMix64Next+  ) where++-- import Data.IORef+import Data.STRef+-- import Data.Coerce+import Control.Monad.ST+import GHC.Types+import GHC.Prim+import Prelude hiding (not)++-- $setup+-- >>> :set -XMagicHash++data XorshiftPlus1 = XorshiftPlus1 Word# Word#++-- | Random state+newtype XorshiftPlusST s = XorshiftPlusST (STRef s XorshiftPlus1)++-- | Random state+type XorshiftPlus = XorshiftPlusST RealWorld++plus :: Word# -> Word# -> Word#+plus = plusWord#++times :: Word# -> Word# -> Word#+times = timesWord#++xor :: Word# -> Word# -> Word#+xor = xor#++shiftL :: Word# -> Int# -> Word#+shiftL = uncheckedShiftL#++shiftR :: Word# -> Int# -> Word#+shiftR = uncheckedShiftRL#++-- | For first return value.+--+-- >>> W# (splitMix64Next 1##)+-- 10451216379200822465+splitMix64Next :: Word# -> Word#+splitMix64Next x =+  let z1 = x `plus` 0x9e3779b97f4a7c15## in+  let z2 = (z1 `xor` (z1 `shiftR` 30#)) `times` 0xbf58476d1ce4e5b9## in+  let z3 = (z2 `xor` (z2 `shiftR` 27#)) `times` 0x94d049bb133111eb## in+  z3 `xor` (z3 `shiftR` 31#)++-- | Generate a new random state by a Word seed.+genXorshiftPlusWordST :: Word -> ST s (XorshiftPlusST s)+genXorshiftPlusWordST (W# w) = do+  ref <- newSTRef (XorshiftPlus1 w (splitMix64Next w))+  return $ XorshiftPlusST ref++-- | Generate a new random state by a Word seed.+genXorshiftPlusWord+  :: Word            -- ^ random seed+  -> IO XorshiftPlus+genXorshiftPlusWord w = stToIO (genXorshiftPlusWordST w)++-- | Generate a new random state by an Int seed.+genXorshiftPlusInt+  :: Int             -- ^ random seed+  -> IO XorshiftPlus+genXorshiftPlusInt i = genXorshiftPlusWord (fromIntegral i)++-- | Get a new random value as Word.+getWordST :: XorshiftPlusST s -> ST s Word+getWordST (XorshiftPlusST ref) = do+  XorshiftPlus1 w0 w1 <- readSTRef ref+  let x = w0 `xor` (w0 `shiftL` 17#)+  let y = x `xor` w1 `xor` (x `shiftR` 17#) `xor` (w1 `shiftR` 26#)+  writeSTRef ref $ XorshiftPlus1 w1 y+  return $ W# (w1 `plus` y)++-- | Get a new random value as Word.+getWord :: XorshiftPlus -> IO Word+getWord = stToIO . getWordST++-- | Get a new random value as Int.+getInt :: XorshiftPlus -> IO Int+getInt x = do+  w <- getWord x+  return $ fromIntegral w++-- | Get a new random value as Double [0, 1.0].+getDouble :: XorshiftPlus -> IO Double+getDouble x = do+  let maxWord = fromIntegral (maxBound :: Word)+  w <- getWord x+  return $ (fromIntegral w) / maxWord
+ tests/doctest/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import Test.DocTest++main :: IO ()+main = doctest ["-isrc", "src/"]
+ tests/spec/Random/XorshiftPlusSpec.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE TypeApplications #-}++module Random.XorshiftPlusSpec where++import Test.Hspec+import Test.QuickCheck+import qualified Test.QuickCheck.Monadic as QC+import Random.XorshiftPlus+import Data.List+import Control.Monad+import System.CPUTime++run :: IO ()+run = do+  seed <- getCPUTime+  xorshift <- genXorshiftPlusInt (fromInteger seed)+  ws <- sequence $ replicate 10 $ getWord xorshift+  print ws+  is <- sequence $ replicate 10 $ getInt xorshift+  print is+  ds <- sequence $ replicate 10 $ getDouble xorshift+  print ds++rangeDouble :: Property+rangeDouble = property $ \seed -> QC.monadicIO $ do+  xorshift <- QC.run $ genXorshiftPlusInt seed+  ret <- QC.run $ sequence $ replicate 100 $ getDouble xorshift+  QC.assert $ all (\r -> 0.0 <= r && r <= 1.0) ret++calcChisq :: [Int] -> Double -> Double+calcChisq count expected = foldl' f 0.0 $ count+  where+    f :: Double -> Int -> Double+    f r x = r + ((((fromIntegral x) - expected)) ** 2 / expected)++testChisq :: Int -> Int -> [Int] -> Double+testChisq n k randoms = calcChisq count (fromIntegral n / fromIntegral k)+  where+    minInt = toInteger (minBound :: Int)+    maxInt = toInteger (maxBound :: Int)+    step = (maxInt - minInt) `div` (toInteger k)+    randoms' = map (\r -> fromInteger @Int ((toInteger r - minInt) `div` step)) randoms+    count = map length . group $ sort randoms'++execTestChisq :: IO Bool+execTestChisq = do+  xorshift <- genXorshiftPlusInt 1+  let (k, limit) = (10, 16.919)+  let n = k * 100+  randoms <- sequence $ replicate n $ getInt xorshift+  let ret = testChisq n k randoms+  return $ ret < limit++testCor :: Int -> Int -> [Double] -> Double+testCor n k randoms = let s = testCor' n randoms 0.0 in+                      let n' = fromIntegral n in+                      sqrt n' * (12.0 * s / n' - 3.0) / sqrt 13.0+  where+    testCor' :: Int -> [Double] -> Double -> Double+    testCor' 0 _       res = res+    testCor' n' randoms' res =+      let (buff, restRandoms) = splitAt k randoms' in+      let x =  head buff * head restRandoms in+      testCor' (n'-1) (tail randoms') (res+x)++execTestCor :: IO Bool+execTestCor = do+  xorshift <- genXorshiftPlusInt 1+  let n = 2500+  randoms <- sequence $ replicate ((n+1) * 3) $ getDouble xorshift+  cors <- forM [1, 2, 3] $ \k -> do+    let x = testCor n k randoms+    return x+  return $ all (\x -> (-1.96 < x && x < 1.96)) cors++spec :: Spec+spec = do+  describe "_run" $ do+    it "_run" $ do+      run+  describe "range" $ do+    it "getDouble" $ do+      rangeDouble+  describe "chisq" $ do+    it "chisq" $ do+      ret <- execTestChisq+      ret `shouldBe` True+  describe "cor" $ do+    it "cor" $ do+      ret <- execTestCor+      ret `shouldBe` True
+ tests/spec/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ xorshift-plus.cabal view
@@ -0,0 +1,70 @@+cabal-version:       2.4+name:                xorshift-plus+version:             0.1.0.0++synopsis:            Simple implementation of xorshift+ PRNG+-- description:++homepage:            https://github.com/syocy/xorshift-plus+bug-reports:         https://github.com/syocy/xorshift-plus/issues++license:             BSD-3-Clause+license-file:        LICENSE++author:              OSANAI Kazuyoshi+maintainer:          osmium.k@gmail.com+-- copyright:++category:            Math++extra-source-files:  README.md+                   , CHANGELOG.md++source-repository head+  type:              git+  location:          https://github.com/syocy/xorshift-plus+              +library+  exposed-modules:     Random.XorshiftPlus+  -- other-extensions:+  build-depends:       base ^>=4.12.0.0+                     , ghc-prim+  hs-source-dirs:      src+  ghc-options:         -Wall -Wcompat -O2+  default-language:    Haskell2010++test-suite doctests+  type:                exitcode-stdio-1.0+  main-is:             Main.hs+  hs-source-dirs:      tests/doctest+  build-depends:       base+                     , doctest >= 0.8+  ghc-options:         -Wall -Wcompat+  default-language:    Haskell2010++test-suite specs+  type:                exitcode-stdio-1.0+  main-is:             Spec.hs+  hs-source-dirs:      tests/spec+  other-modules:       Random.XorshiftPlusSpec+  build-depends:       base+                     , xorshift-plus+                     , hspec >= 2+                     , QuickCheck+  build-tool-depends:  hspec-discover:hspec-discover >= 2+  ghc-options:         -Wall -Wcompat+  default-language:    Haskell2010++benchmark micro+  type:                exitcode-stdio-1.0+  main-is:             micro.hs+  hs-source-dirs:      benchmarks/micro+  build-depends:       base+                     , xorshift-plus+                     , gauge >= 0.2+                     , xorshift >= 2+                     , random >= 1.1+                     , Xorshift128Plus == 0.1.0.2+  ghc-options:         -Wall -Wcompat -O2+  default-language:    Haskell2010+