packages feed

rvar 0.2.0.6 → 0.3.0.0

raw patch · 4 files changed

+205/−88 lines, 4 filesdep +bytestringdep +randomdep −random-sourcedep ~basedep ~mtl

Dependencies added: bytestring, random

Dependencies removed: random-source

Dependency ranges changed: base, mtl

Files

+ changelog.md view
@@ -0,0 +1,15 @@+* Changes in 0.3.0.0:++  * Drop usage of `random-source` in favor of `random`+  * Add `Prim` type that resembles one from `random-source`+  * Add `RGen` type that serves the same purpose as `StdRandom` in `random-source`++* Changes in 0.2.0.6: None. (Pacify Hackage.)++* Changes in 0.2.0.4: Update for GHC 8.8.++* Changes in 0.2.0.3: Version bump for transformers dependency.++* Changes in 0.2.0.2: Version bump for transformers dependency.++* Changes in 0.2.0.1: Version bump for transformers dependency.
rvar.cabal view
@@ -1,12 +1,12 @@ name:                   rvar-version:                0.2.0.6+version:                0.3.0.0 stability:              stable  cabal-version:          >= 1.10 build-type:             Simple  author:                 James Cook <mokus@deepbondi.net>-maintainer:             James Cook <mokus@deepbondi.net>+maintainer:             Dominic Steinitz <dominic@steinitz.org> license:                PublicDomain homepage:               https://github.com/mokus0/random-fu @@ -26,16 +26,14 @@                         comparable to other Haskell libraries, but still                         a fair bit slower than straight C implementations of                         the same algorithms.-                        .-                        Changes in 0.2.0.1:  Version bump for transformers-                        dependency. -tested-with:            GHC == 6.8.3, GHC == 6.10.4, GHC == 6.12.3,-                        GHC == 7.0.4, GHC == 7.2.1, GHC == 7.2.2+tested-with:            GHC == 8.10.7 +extra-source-files:     changelog.md+ source-repository head   type:                 git-  location:             https://github.com/mokus0/random-fu.git+  location:             https://github.com/haskell-numerics/random-fu   subdir:               rvar  Flag mtl2@@ -46,6 +44,7 @@   hs-source-dirs:       src   default-language:     Haskell2010   exposed-modules:      Data.RVar+  other-modules:        Data.RVar.Prim    if flag(mtl2)     build-depends:      mtl == 2.*@@ -54,6 +53,7 @@     build-depends:      mtl == 1.1.*    build-depends:        base            >= 3 && <5,+                        bytestring,                         MonadPrompt     == 1.0.*,-                        random-source   == 0.3.*,-                        transformers    >= 0.2 && < 0.6+                        transformers    >= 0.2 && < 0.6,+                        random          >= 1.2.0
src/Data/RVar.hs view
@@ -1,112 +1,121 @@ {-  -      ``Data/Random/RVar''  -}-{-# LANGUAGE-    RankNTypes,-    MultiParamTypeClasses,-    FlexibleInstances, -    GADTs,-    ScopedTypeVariables,-    CPP-  #-}+{-# 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-    ( RandomSource-    , MonadRandom-        ( getRandomWord8-        , getRandomWord16-        , getRandomWord32-        , getRandomWord64-        , getRandomDouble-        , getRandomNByteInteger-        )-    -    , RVar-    , runRVar, sampleRVar-    +    ( RVar+    , runRVar, sampleReaderRVar, sampleStateRVar+    , pureRVar+     , RVarT-    , runRVarT, sampleRVarT-    , runRVarTWith, sampleRVarTWith-    ) where+    , runRVarT, sampleReaderRVarT, sampleStateRVarT+    , runRVarTWith, sampleReaderRVarTWith, sampleStateRVarTWith +    , RGen(..)+    , uniformRVarT+    , uniformRangeRVarT -import Data.Random.Internal.Source (Prim(..), MonadRandom(..), RandomSource(..))-import Data.Random.Source ({-instances-})+    , Prim(..)+    ) where -import qualified Control.Monad.Trans.Class as T-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 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 --- |An opaque type modeling a \"random variable\" - a value --- which depends on the outcome of some random event.  'RVar's +-- |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)+-- * Using an immutable pseudo-random number generator that has an instance for `RandomGen` with+--   `StateT` monad: ----- (where @sampleState = runState . sampleRVar@)+-- >>> 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 :: RandomSource m s => RVar a -> s -> m a+runRVar :: StatefulGen g m => RVar a -> g -> 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)+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:@@ -119,35 +128,39 @@ -- -- 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 = +-- > 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 :: StatefulGen g m => RVarT m a -> g -> m a runRVarT = runRVarTWith id -sampleRVarT :: MonadRandom m => RVarT m a -> m a-sampleRVarT = sampleRVarTWith 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 +--+-- The first argument lifts the base monad into the sampling monad.  This -- operation must obey the \"monad transformer\" laws: -- -- > lift . return = return@@ -166,30 +179,62 @@ -- -- 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 +-- 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 +-- 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+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 = getRandomPrimFrom src prim >>= cont-        +        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'@.-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+{-# 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 = getRandomPrim prim >>= cont-        +        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 @@ -197,9 +242,6 @@     return x = RVarT (return $! x)     (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@@ -222,3 +264,26 @@     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 #-}
+ src/Data/RVar/Prim.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+-- |This is an internal interface to support the 'RVar' abstraction.  It+-- reifies the operations provided by `System.Random.Stateful.StatefulGen` in a+-- uniform and efficient way, as functions of type @Prim a -> m a@.+module Data.RVar.Prim (Prim(..)) where++import Data.Typeable+import Data.Word+import Data.ByteString.Short++-- |A 'Prompt' GADT describing a request for a primitive random variate.  Random variable+-- definitions will request their entropy via these prompts, and entropy sources will+-- satisfy those requests. This data type is needed for creating+-- `System.Random.Stateful.StatefulGen` instance for `Data.RVar.RVarT`+--+data Prim a where+    -- | An unsigned byte, uniformly distributed from 0 to 0xff+    PrimWord8           :: Prim Word8+    -- | An unsigned 16-bit word, uniformly distributed from 0 to 0xffff+    PrimWord16          :: Prim Word16+    -- | An unsigned 32-bit word, uniformly distributed from 0 to 0xffffffff+    PrimWord32          :: Prim Word32+    -- | An unsigned 64-bit word, uniformly distributed from 0 to 0xffffffffffffffff+    PrimWord64          :: Prim Word64+    -- | A uniformly distributed `ShortByteString` of length @n@ bytes+    PrimShortByteString :: !Int -> Prim ShortByteString+    deriving (Typeable)++instance Show (Prim a) where+    showsPrec _p PrimWord8         = showString "PrimWord8"+    showsPrec _p PrimWord16        = showString "PrimWord16"+    showsPrec _p PrimWord32        = showString "PrimWord32"+    showsPrec _p PrimWord64        = showString "PrimWord64"+    showsPrec  p (PrimShortByteString n) =+      showParen (p > 10) (showString "PrimShortByteString " . showsPrec 11 n)