8

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?

Yrogirg
  • 2,301
  • 3
  • 22
  • 33

3 Answers3

8

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
Thies Heidecke
  • 2,497
  • 1
  • 23
  • 25
  • 2
    You can do that for any `RandomGen` (of which `PureMT` is an instance), with `randoms :: (RandomGen g, Random a) => g -> [a]`. – ehird Jan 08 '12 at 13:01
  • @ehird oh, didn't know about `randoms`! Updated my answer – Thies Heidecke Jan 08 '12 at 14:34
  • 1
    There is also `monad-mersenne-random` http://hackage.haskell.org/package/monad-mersenne-random built on top of `mersenne-random-pure64`. It has a nice property that it allows to run monadic iteration in constant memory space (see this question: http://stackoverflow.com/questions/3236442/). – sastanin Feb 05 '13 at 12:36
  • @sastanin Thanks for pointing me to the related question and `monad-mersenne-random`, gave me some new insights! – Thies Heidecke Feb 06 '13 at 10:06
5

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.

ehird
  • 40,602
  • 3
  • 180
  • 182
  • @ehird What is here the IO free source of randomness that would keep it pure? ECC? – J Fritsch Jan 08 '12 at 09:44
  • @JFritsch: Remember, pure doesn't just mean no IO. A pure function must _always_ return the same result given the same arguments. The only "source of randomness" is the generator or _seed_ (`g` in the type signatures). – hammar Jan 08 '12 at 09:51
  • @JFritsch: No PRNG consults an external source for each number; you just get the seed from there. You can create values of type `StdGen` (an instance of `RandomGen`) from a seed, and there's a global generator in IO presumably seeded from an external source. – ehird Jan 08 '12 at 10:12
  • @ehird for P RNGs you are certainly right. Otherwise "no" is a bit strong :D http://www.sqrtech.com/. Good randomness is an issue in a web2.0 world and IO plays a heavy role in it. – J Fritsch Jan 09 '12 at 08:26
2

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.

ertes
  • 2,287
  • 15
  • 10