packages feed

rvar (empty) → 0.2

raw patch · 3 files changed

+287/−0 lines, 3 filesdep +MonadPromptdep +basedep +mtlsetup-changed

Dependencies added: MonadPrompt, base, mtl, random-source, transformers

Files

+ Setup.lhs view
@@ -0,0 +1,5 @@+#!/usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMain+
+ rvar.cabal view
@@ -0,0 +1,55 @@+name:                   rvar+version:                0.2+stability:              stable++cabal-version:          >= 1.6+build-type:             Simple++author:                 James Cook <james.cook@usma.edu>+maintainer:             James Cook <james.cook@usma.edu>+license:                PublicDomain+homepage:               https://github.com/mokus0/random-fu++category:               Math+synopsis:               Random Variables+description:            Random number generation based on modeling random +                        variables by an abstract type ('RVar') which can be+                        composed and manipulated monadically and sampled in+                        either monadic or \"pure\" styles.+                        +                        The primary purpose of this library is to support +                        defining and sampling a wide variety of high quality+                        random variables.  Quality is prioritized over speed,+                        but performance is an important goal too.+                        +                        In my testing, I have found it capable of speed +                        comparable to other Haskell libraries, but still+                        a fair bit slower than straight C implementations of +                        the same algorithms.++tested-with:            GHC == 6.8.3, GHC == 6.10.4, GHC == 6.12.1,+                        GHC == 6.12.3, GHC == 7.0.1, GHC == 7.0.2++source-repository head+  type:                 git+  location:             https://github.com/mokus0/random-fu.git+  subdir:               rvar++Flag mtl2+    Description:        mtl-2 has State, etc., as "type" rather than "newtype"++Library+  ghc-options:          -Wall+  hs-source-dirs:       src+  exposed-modules:      Data.RVar++  if flag(mtl2)+    build-depends:      mtl == 2.*+    cpp-options:        -DMTL2+  else+    build-depends:      mtl == 1.1.*+  +  build-depends:        base            >= 3 && <5,+                        MonadPrompt     == 1.0.*,+                        random-source   == 0.3.*,+                        transformers    == 0.2.*
+ src/Data/RVar.hs view
@@ -0,0 +1,227 @@+{-+ -      ``Data/Random/RVar''+ -}+{-# LANGUAGE+    RankNTypes,+    MultiParamTypeClasses,+    FlexibleInstances, +    GADTs,+    ScopedTypeVariables,+    CPP+  #-}++-- |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.+module Data.RVar+    ( RandomSource+    , MonadRandom+        ( getRandomWord8+        , getRandomWord16+        , getRandomWord32+        , getRandomWord64+        , getRandomDouble+        , getRandomNByteInteger+        )+    +    , RVar+    , runRVar, sampleRVar+    +    , RVarT+    , runRVarT, sampleRVarT+    , runRVarTWith, sampleRVarTWith+    ) where+++import Data.Random.Internal.Source (Prim(..), MonadRandom(..), RandomSource(..))+import Data.Random.Source ({-instances-})++import qualified Control.Monad.Trans.Class as T+import Control.Applicative+import Control.Monad (liftM, ap)+import Control.Monad.Prompt (MonadPrompt(..), PromptT, runPromptT)+import qualified Control.Monad.IO.Class as T+import qualified Control.Monad.Trans as MTL+import qualified Control.Monad.Identity as MTL+import qualified Data.Functor.Identity as T++-- |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:+-- +-- * In a monad, using a 'RandomSource':+-- +-- > runRVar (uniform 1 100) DevRandom :: IO Int+-- +-- * In a monad, using a 'MonadRandom' instance:+--+-- > 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@)+type RVar = RVarT T.Identity++-- |\"Run\" an 'RVar' - samples the random variable from the provided+-- source of entropy.+runRVar :: RandomSource m s => RVar a -> s -> m a+runRVar = runRVarTWith (return . T.runIdentity)++-- |@sampleRVar x@ is equivalent to @runRVar x 'StdRandom'@.+sampleRVar :: MonadRandom m => RVar a -> m a+sampleRVar = sampleRVarTWith (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 :: RandomSource m s => RVarT m a -> s -> m a+runRVarT = runRVarTWith id++sampleRVarT :: MonadRandom m => RVarT m a -> m a+sampleRVarT = sampleRVarTWith id++-- | \"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 s a. RandomSource m s => (forall t. n t -> m t) -> RVarT n a -> s -> m a+runRVarTWith liftN (RVarT m) src = runPromptT return bindP bindN m+    where+        bindP :: forall t. (Prim t -> (t -> m a) -> m a)+        bindP prim cont = getRandomPrimFrom src 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'@.+sampleRVarTWith :: forall m n a. MonadRandom m => (forall t. n t -> m t) -> RVarT n a -> m a+sampleRVarTWith liftN (RVarT m) = runPromptT return bindP bindN m+    where+        bindP :: forall t. (Prim t -> (t -> m a) -> m a)+        bindP prim cont = getRandomPrim prim >>= 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+    return x = RVarT (return $! x)+    fail s   = RVarT (fail s)+    (RVarT m) >>= k = RVarT (m >>= \x -> x `seq` unRVarT (k x))++instance MonadRandom (RVarT n) where+    getRandomPrim = RVarT . prompt++instance Applicative (RVarT n) where+    pure  = return+    (<*>) = 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