rvar-0.3.0.1: src/Data/RVar.hs
{-
- ``Data/Random/RVar''
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- |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
-- 'RVar's.
{-# LANGUAGE FlexibleContexts #-}
module Data.RVar
( RVar
, runRVar, sampleReaderRVar, sampleStateRVar
, pureRVar
, RVarT
, runRVarT, sampleReaderRVarT, sampleStateRVarT
, runRVarTWith, sampleReaderRVarTWith, sampleStateRVarTWith
, RGen(..)
, uniformRVarT
, uniformRangeRVarT
, Prim(..)
) where
import qualified Control.Monad.IO.Class as T
import Control.Monad.Prompt (MonadPrompt(..), PromptT, runPromptT)
import Control.Monad.Reader as MTL
import Control.Monad.State as MTL
import qualified Control.Monad.Trans.Class as T
import qualified Data.Functor.Identity as T
import Data.RVar.Prim
import System.Random.Stateful
import Control.Monad (ap, liftM)
-- |An opaque type modeling a \"random variable\" - a value
-- which depends on the outcome of some random event. 'RVar's
-- 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 'RVar's:
--
-- * Using an immutable pseudo-random number generator that has an instance for `RandomGen` with
-- `StateT` monad:
--
-- >>> import qualified Data.Random as Fu (uniform)
-- >>> import System.Random (mkStdGen)
-- >>> import Control.Monad.State (runState)
-- >>> runState (sampleStateRVar (Fu.uniform 1 (100 :: Integer))) (mkStdGen 2021)
-- (79,StdGen {unStdGen = SMGen 4687568268719557181 4805600293067301895})
--
-- * Using a mutable pseud-random number generator that has an instance for `StatefulGen` with
-- `ReaderT` monad.
--
-- >>> import qualified Data.Random as Fu (uniform)
-- >>> import System.Random.MWC (create)
-- >>> import Control.Monad.Reader (runReaderT)
-- >>> import qualified Data.Vector.Storable as VS
-- >>> initialize (VS.singleton 2021) >>= runReaderT (sampleReaderRVar (uniform 1 (100 :: Integer)))
-- 8
--
type RVar = RVarT T.Identity
-- | Sample random variable using `RandomGen` generator as source of entropy
pureRVar :: RandomGen g => RVar a -> g -> (a, g)
pureRVar rvar g = runStateGen g (runRVar rvar)
-- |\"Run\" an 'RVar' - samples the random variable from the provided
-- source of entropy.
runRVar :: StatefulGen g m => RVar a -> g -> m a
runRVar = runRVarTWith (return . T.runIdentity)
-- |@sampleRVar x@ is equivalent to @runRVar x 'StdRandom'@.
sampleReaderRVar :: (StatefulGen g m, MonadReader g m) => RVar a -> m a
sampleReaderRVar = sampleReaderRVarTWith (return . T.runIdentity)
sampleStateRVar :: (RandomGen g, MonadState g m) => RVar a -> m a
sampleStateRVar = sampleStateRVarTWith (return . T.runIdentity)
-- |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
newtype RVarT m a = RVarT { unRVarT :: PromptT Prim m a }
runRVarT :: StatefulGen g m => RVarT m a -> g -> m a
runRVarT = runRVarTWith id
sampleStateRVarT :: (RandomGen g, MonadState g m) => RVarT m a -> m a
sampleStateRVarT rvar = runRVarT rvar StateGenM
sampleReaderRVarT :: (StatefulGen g m, MonadReader g m) => RVarT m a -> m a
sampleReaderRVarT rvar = ask >>= runRVarT rvar
-- | \"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 'RVar's would have to use higher-ranked
-- types to enforce the same kind of isolation and polymorphism.
{-# INLINE runRVarTWith #-}
runRVarTWith :: forall m n g a. StatefulGen g m => (forall t. n t -> m t) -> RVarT n a -> g -> m a
runRVarTWith liftN (RVarT m) gen = runPromptT return bindP bindN m
where
bindP :: forall t. (Prim t -> (t -> m a) -> m a)
bindP prim cont = uniformPrimM prim gen >>= cont
bindN :: forall t. n t -> (t -> m a) -> m a
bindN nExp cont = liftN nExp >>= cont
{-# INLINE uniformPrimM #-}
uniformPrimM :: StatefulGen g m => Prim t -> g -> m t
uniformPrimM prim g =
case prim of
PrimWord8 -> uniformWord8 g
PrimWord16 -> uniformWord16 g
PrimWord32 -> uniformWord32 g
PrimWord64 -> uniformWord64 g
PrimShortByteString n -> uniformShortByteString n g
-- |@sampleRVarTWith lift x@ is equivalent to @runRVarTWith lift x 'StdRandom'@.
{-# INLINE sampleReaderRVarTWith #-}
sampleReaderRVarTWith ::
forall m n a g. (StatefulGen g m, MonadReader g m)
=> (forall t. n t -> m t)
-> RVarT n a
-> m a
sampleReaderRVarTWith liftN (RVarT m) = runPromptT return bindP bindN m
where
bindP :: forall t. (Prim t -> (t -> m a) -> m a)
bindP prim cont = ask >>= uniformPrimM prim >>= cont
bindN :: forall t. n t -> (t -> m a) -> m a
bindN nExp cont = liftN nExp >>= cont
-- |@sampleRVarTWith lift x@ is equivalent to @runRVarTWith lift x 'StdRandom'@.
{-# INLINE sampleStateRVarTWith #-}
sampleStateRVarTWith ::
forall m n a g. (RandomGen g, MonadState g m)
=> (forall t. n t -> m t)
-> RVarT n a
-> m a
sampleStateRVarTWith liftN (RVarT m) = runPromptT return bindP bindN m
where
bindP :: forall t. (Prim t -> (t -> m a) -> m a)
bindP prim cont = uniformPrimM prim StateGenM >>= cont
bindN :: forall t. n t -> (t -> m a) -> m a
bindN nExp cont = liftN nExp >>= cont
instance Functor (RVarT n) where
fmap = liftM
instance Monad (RVarT n) where
(RVarT m) >>= k = RVarT (m >>= \x -> x `seq` unRVarT (k x))
instance Applicative (RVarT n) where
pure x = RVarT (pure $! x)
(<*>) = ap
instance MonadPrompt Prim (RVarT n) where
prompt = RVarT . prompt
instance T.MonadTrans RVarT where
lift m = RVarT (MTL.lift m)
instance T.MonadIO m => T.MonadIO (RVarT m) where
liftIO = T.lift . T.liftIO
#ifndef MTL2
instance MTL.MonadTrans RVarT where
lift m = RVarT (MTL.lift m)
instance MTL.MonadIO m => MTL.MonadIO (RVarT m) where
liftIO = MTL.lift . MTL.liftIO
#endif
data RGen = RGen
instance StatefulGen RGen (RVarT m) where
uniformWord8 RGen = RVarT $ prompt PrimWord8
{-# INLINE uniformWord8 #-}
uniformWord16 RGen = RVarT $ prompt PrimWord16
{-# INLINE uniformWord16 #-}
uniformWord32 RGen = RVarT $ prompt PrimWord32
{-# INLINE uniformWord32 #-}
uniformWord64 RGen = RVarT $ prompt PrimWord64
{-# INLINE uniformWord64 #-}
uniformShortByteString n RGen = RVarT $ prompt (PrimShortByteString n)
{-# INLINE uniformShortByteString #-}
uniformRVarT :: Uniform a => RVarT m a
uniformRVarT = uniformM RGen
{-# INLINE uniformRVarT #-}
uniformRangeRVarT :: UniformRange a => (a, a) -> RVarT m a
uniformRangeRVarT r = uniformRM r RGen
{-# INLINE uniformRangeRVarT #-}