Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pure pseudo-random generators for Haskell with a nice API?

Tags:

random

haskell

What are the recommended Haskell packages for pure pseudo-random generators (uniform Doubles)?

I'm interested in a convenient API in the first place, speed would be nice too.

Maybe mwc-random?

like image 337
Yrogirg Avatar asked Jan 08 '12 09:01

Yrogirg


3 Answers

I like the mersenne-random-pure64 package. For example you can use it like this to generate an infinite lazy stream of random doubles from a seed value:

import Data.Word (Word64)
import Data.List (unfoldr)
import System.Random.Mersenne.Pure64

randomStream :: (PureMT -> (a, PureMT)) -> PureMT -> [a]
randomStream rndstep g = unfoldr (Just . rndstep) g

toStream :: Word64 -> [Double]
toStream seed = randomStream randomDouble $ pureMT seed

main = print . take 10 $ toStream 42

using System.Random (randoms)

You can get a similar output with the builtin randoms function, which is shorter and more general (Thanks to ehird for pointing that out):

import System.Random (randoms)
import System.Random.Mersenne.Pure64 (pureMT)

main = print . take 10 $ randomdoubles where
  randomdoubles :: [Double]
  randomdoubles = randoms $ pureMT 42

making it an instance of MonadRandom

After reading about MonadRandom i got curious how to get PureMT working as an instance of it. Out of the box it doesn't work, because PureMT doesn't instantiate RandomGen's split function. One way to make it work is to wrap PureMT in a newtype and write a custom split instance for the RandomGen typeclass, for which there exists a default MonadRandom instance.

import Control.Monad.Random
import System.Random.Mersenne.Pure64

getTenRandomDoubles :: Rand MyPureMT [Double]
getTenRandomDoubles = getRandoms >>= return . take 10

main = print $ evalRand getTenRandomDoubles g
  where g = MyPureMT $ pureMT 42


newtype MyPureMT = MyPureMT { unMyPureMT :: PureMT }
myPureMT = MyPureMT . pureMT

instance RandomGen MyPureMT where
  next  = nextMyPureMT
  split = splitMyPureMT

splitMyPureMT :: MyPureMT -> (MyPureMT, MyPureMT)
splitMyPureMT (MyPureMT g) = (myPureMT s, myPureMT s') where
  (s',g'') = randomWord64 g'
  (s ,g' ) = randomWord64 g

nextMyPureMT (MyPureMT g) = (s, MyPureMT g') where
  (s, g') = randomInt g
like image 90
Thies Heidecke Avatar answered Nov 12 '22 03:11

Thies Heidecke


The standard System.Random has a pure interface. I would recommend wrapping it in a State g (for whatever generator g you're using) to avoid threading the state; the state function makes turning functions like next into stateful actions easy:

next :: (RandomGen g) => g -> (Int, g)
state :: (s -> (a, s)) -> State s a
state next :: (RandomGen g) => State g Int

The MonadRandom package is based on the State g interface with pre-written wrappers for the generator functions; I think it's fairly popular.

Note that you can still run actions using this pure interface on the global RNG. MonadRandom has evalRandIO for the purpose.

I think you could write an (orphan) RandomGen instance to use mwc-random with these.

like image 5
ehird Avatar answered Nov 12 '22 03:11

ehird


A particularly nice package with a pure interface that is also suitable for cryptographic applications and yet maintains a high performance is the cprng-aes package.

It provides two interfaces: A deterministic pure one using the type classes from System.Random as well as a strong IO interface using the type classes from the Crypto-API package.

As a side note: I would generally prefer the mersenne-random packages over mwc-random. They use the original Mersenne Twister algorithm and in my benchmarks outperformed mwc-random by a large factor.

like image 2
ertes Avatar answered Nov 12 '22 02:11

ertes