rvar-0.2.0.1: Random Variables

Safe HaskellSafe-Infered

Data.RVar

Description

Random variables. An RVar is a sampleable random variable. Because probability distributions form a monad, they are quite easy to work with in the standard Haskell monadic styles. For examples, see the source for any of the Distribution instances - they all are defined in terms of RVars.

Synopsis

Documentation

class Monad m => RandomSource m s

A source of entropy which can be used in the given monad.

See also MonadRandom.

Minimum implementation is either the internal getRandomPrimFrom or all other functions. Additionally, this class's interface is subject to extension at any time, so it is very, very strongly recommended that the randomSource Template Haskell function be used to implement this function rather than directly implementing it. That function takes care of choosing default implementations for any missing functions; as long as at least one function is implemented, it will derive sensible implementations of all others.

To use randomSource, just wrap your instance declaration as follows (and enable the TemplateHaskell, MultiParamTypeClasses and GADTs language extensions, as well as any others required by your instances, such as FlexibleInstances):

 $(randomSource [d|
         instance RandomSource FooM Bar where
             {- at least one RandomSource function... -}
     |])

Instances

Monad m0 => RandomSource m0 (m0 Double) 
Monad m0 => RandomSource m0 (m0 Word64) 
Monad m0 => RandomSource m0 (m0 Word32) 
Monad m0 => RandomSource m0 (m0 Word16) 
Monad m0 => RandomSource m0 (m0 Word8) 
Monad m => RandomSource m (GetPrim m) 

class Monad m => MonadRandom m where

A typeclass for monads with a chosen source of entropy. For example, RVar is such a monad - the source from which it is (eventually) sampled is the only source from which a random variable is permitted to draw, so when directly requesting entropy for a random variable these functions are used.

Minimum implementation is either the internal getRandomPrim or all other functions. Additionally, this class's interface is subject to extension at any time, so it is very, very strongly recommended that the monadRandom Template Haskell function be used to implement this function rather than directly implementing it. That function takes care of choosing default implementations for any missing functions; as long as at least one function is implemented, it will derive sensible implementations of all others.

To use monadRandom, just wrap your instance declaration as follows (and enable the TemplateHaskell and GADTs language extensions):

 $(monadRandom [d|
         instance MonadRandom FooM where
             getRandomDouble = return pi
             getRandomWord16 = return 4
             {- etc... -}
     |])

Methods

getRandomWord8 :: m Word8

Generate a uniformly distributed random Word8

getRandomWord16 :: m Word16

Generate a uniformly distributed random Word16

getRandomWord32 :: m Word32

Generate a uniformly distributed random Word32

getRandomWord64 :: m Word64

Generate a uniformly distributed random Word64

getRandomDouble :: m Double

Generate a uniformly distributed random Double in the range 0 <= U < 1

getRandomNByteInteger :: Int -> m Integer

Generate a uniformly distributed random Integer in the range 0 <= U < 256^n

Instances

type RVar = RVarT IdentitySource

An opaque type modeling a "random variable" - a value which depends on the outcome of some random event. RVars can be conveniently defined by an imperative-looking style:

 normalPair =  do
     u <- stdUniform
     t <- stdUniform
     let r = sqrt (-2 * log u)
         theta = (2 * pi) * t
         
         x = r * cos theta
         y = r * sin theta
     return (x,y)

OR by a more applicative style:

 logNormal = exp <$> stdNormal

Once defined (in any style), there are several ways to sample RVars:

 runRVar (uniform 1 100) DevRandom :: IO Int
 sampleRVar (uniform 1 100) :: State PureMT Int
  • As a pure function transforming a functional RNG:
 sampleState (uniform 1 100) :: StdGen -> (Int, StdGen)

(where sampleState = runState . sampleRVar)

runRVar :: RandomSource m s => RVar a -> s -> m aSource

"Run" an RVar - samples the random variable from the provided source of entropy.

sampleRVar :: MonadRandom m => RVar a -> m aSource

sampleRVar x is equivalent to runRVar x StdRandom.

data RVarT m a Source

A random variable with access to operations in an underlying monad. Useful examples include any form of state for implementing random processes with hysteresis, or writer monads for implementing tracing of complicated algorithms.

For example, a simple random walk can be implemented as an RVarT IO value:

 rwalkIO :: IO (RVarT IO Double)
 rwalkIO d = do
     lastVal <- newIORef 0
     
     let x = do
             prev    <- lift (readIORef lastVal)
             change  <- rvarT StdNormal
             
             let new = prev + change
             lift (writeIORef lastVal new)
             return new
         
     return x

To run the random walk it must first be initialized, after which it can be sampled as usual:

 do
     rw <- rwalkIO
     x <- sampleRVarT rw
     y <- sampleRVarT rw
     ...

The same random-walk process as above can be implemented using MTL types as follows (using import Control.Monad.Trans as MTL):

 rwalkState :: RVarT (State Double) Double
 rwalkState = do
     prev <- MTL.lift get
     change  <- rvarT StdNormal
     
     let new = prev + change
     MTL.lift (put new)
     return new

Invocation is straightforward (although a bit noisy) if you're used to MTL:

 rwalk :: Int -> Double -> StdGen -> ([Double], StdGen)
 rwalk count start gen = 
     flip evalState start .
         flip runStateT gen .
             sampleRVarTWith MTL.lift $
                 replicateM count rwalkState

runRVarT :: RandomSource m s => RVarT m a -> s -> m aSource

runRVarTWith :: forall m n s a. RandomSource m s => (forall t. n t -> m t) -> RVarT n a -> s -> m aSource

"Runs" an RVarT, sampling the random variable it defines.

The first argument lifts the base monad into the sampling monad. This operation must obey the "monad transformer" laws:

 lift . return = return
 lift (x >>= f) = (lift x) >>= (lift . f)

One example of a useful non-standard lifting would be one that takes State s to another monad with a different state representation (such as IO with the state mapped to an IORef):

 embedState :: (Monad m) => m s -> (s -> m ()) -> State s a -> m a
 embedState get put = \m -> do
     s <- get
     (res,s) <- return (runState m s)
     put s
     return res

The ability to lift is very important - without it, every RVar would have to either be given access to the full capability of the monad in which it will eventually be sampled (which, incidentally, would also have to be monomorphic so you couldn't sample one RVar in more than one monad) or functions manipulating RVars would have to use higher-ranked types to enforce the same kind of isolation and polymorphism.

sampleRVarTWith :: forall m n a. MonadRandom m => (forall t. n t -> m t) -> RVarT n a -> m aSource

sampleRVarTWith lift x is equivalent to runRVarTWith lift x StdRandom.