{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE StandaloneDeriving #-} -- | The @Random@ effect provides access to uniformly distributed random values of -- user-specified types or from well-known numerical distributions. -- -- This is the “fancy” syntax that hides most details of randomness -- behind a nice API. module Control.Effect.Random ( Random (..), -- * Uniform distributions uniform, uniformR, -- * Continuous distributions normal, standard, exponential, truncatedExp, gamma, chiSquare, beta, -- * Discrete distributions categorical, logCategorical, geometric0, geometric1, bernoulli, dirichlet, -- * Permutations uniformPermutation, uniformShuffle, -- * Introspection save, Distrib (..), -- * Re-exports MWC.Variate, Has, ) where import Control.Algebra import Data.Kind import Data.Vector.Generic (Vector) import qualified System.Random.MWC as MWC -- | GADT representing the functions provided by mwc-random. data Distrib a where Uniform :: MWC.Variate a => Distrib a UniformR :: MWC.Variate a => (a, a) -> Distrib a Normal :: Double -> Double -> Distrib Double Standard :: Distrib Double Exponential :: Double -> Distrib Double TruncatedExp :: Double -> (Double, Double) -> Distrib Double Gamma :: Double -> Double -> Distrib Double ChiSquare :: Int -> Distrib Double Beta :: Double -> Double -> Distrib Double Categorical :: Vector v Double => v Double -> Distrib Int LogCategorical :: Vector v Double => v Double -> Distrib Int Geometric0 :: Double -> Distrib Int Geometric1 :: Double -> Distrib Int Bernoulli :: Double -> Distrib Bool Dirichlet :: Traversable t => t Double -> Distrib (t Double) Permutation :: Vector v Int => Int -> Distrib (v Int) Shuffle :: Vector v a => v a -> Distrib (v a) data Random (m :: Type -> Type) k where Random :: Distrib a -> Random m a Save :: Random m MWC.Seed -- | Generate a single uniformly distributed random variate. The -- range of values produced varies by type: -- -- * For fixed-width integral types, the type's entire range is -- used. -- -- * For floating point numbers, the range (0,1] is used. Zero is -- explicitly excluded, to allow variates to be used in -- statistical calculations that require non-zero values -- (e.g. uses of the 'log' function). -- -- To generate a 'Float' variate with a range of [0,1), subtract -- 2**(-33). To do the same with 'Double' variates, subtract -- 2**(-53). uniform :: (MWC.Variate a, Has Random sig m) => m a uniform = send (Random Uniform) {-# INLINE uniform #-} -- | Generate single uniformly distributed random variable in a -- given range. -- -- * For integral types inclusive range is used. -- -- * For floating point numbers range (a,b] is used if one ignores -- rounding errors. uniformR :: (MWC.Variate a, Has Random sig m) => (a, a) -> m a uniformR r = send (Random (UniformR r)) {-# INLINE uniformR #-} -- | Generate a normally distributed random variate with given mean and standard deviation. normal :: Has Random sig m => -- | Mean Double -> -- | Standard deviation Double -> m Double normal m d = send (Random (Normal m d)) -- | Generate a normally distributed random variate with zero mean and unit variance. standard :: Has Random sig m => m Double standard = send (Random Standard) -- | Generate an exponentially distributed random variate. exponential :: Has Random sig m => -- | Scale parameter Double -> m Double exponential s = send (Random (Exponential s)) -- | Generate truncated exponentially distributed random variate. truncatedExp :: Has Random sig m => -- | Scale parameter Double -> -- | Range to which distribution is -- truncated. Values may be negative. (Double, Double) -> m (Double) truncatedExp s r = send (Random (TruncatedExp s r)) -- | Random variate generator for gamma distribution. gamma :: Has Random sig m => -- | Shape parameter Double -> -- | Scale parameter Double -> m Double gamma s d = send (Random (Gamma s d)) -- | Random variate generator for the chi square distribution. chiSquare :: Has Random sig m => -- | Number of degrees of freedom Int -> m Double chiSquare d = send (Random (ChiSquare d)) -- | Random variate generator for the geometric distribution, -- computing the number of failures before success. Distribution's -- support is [0..]. geometric0 :: Has Random sig m => -- | /p/ success probability lies in (0,1] Double -> m Int geometric0 p = send (Random (Geometric0 p)) -- | Random variate generator for geometric distribution for number of -- trials. Distribution's support is [1..] (i.e. just 'geometric0' -- shifted by 1). geometric1 :: Has Random sig m => -- | /p/ success probability lies in (0,1] Double -> m Int geometric1 p = send (Random (Geometric1 p)) -- | Random variate generator for Beta distribution beta :: Has Random sig m => -- | alpha (>0) Double -> -- | beta (>0) Double -> m Double beta a b = send (Random (Beta a b)) {-# INLINE beta #-} -- | Random variate generator for Dirichlet distribution dirichlet :: (Has Random sig m, Traversable t) => -- | container of parameters t Double -> m (t Double) {-# INLINE dirichlet #-} dirichlet t = send (Random (Dirichlet t)) -- | Random variate generator for Bernoulli distribution bernoulli :: Has Random sig m => -- | Probability of success (returning True) Double -> m Bool {-# INLINE bernoulli #-} bernoulli p = send (Random (Bernoulli p)) -- | Random variate generator for categorical distribution. categorical :: (Has Random sig m, Vector v Double) => -- | List of weights [>0] v Double -> m Int {-# INLINE categorical #-} categorical v = send (Random (Categorical v)) -- | Random variate generator for categorical distribution where the -- weights are in the log domain. It's implemented in terms of -- 'categorical'. logCategorical :: (Has Random sig m, Vector v Double) => -- | List of logarithms of weights v Double -> m Int logCategorical v = send (Random (LogCategorical v)) -- | Save the state of the random number generator to be used by subsequent -- carrier invocations. save :: Has Random sig m => m MWC.Seed save = send Save -- | Random variate generator for uniformly distributed permutations. It returns random permutation of vector [0 .. n-1]. This is the Fisher-Yates shuffle. uniformPermutation :: (Has Random sig m, Vector v Int) => Int -> m (v Int) uniformPermutation n = send (Random (Permutation n)) -- | Random variate generator for a uniformly distributed shuffle (all -- shuffles are equiprobable) of a vector. It uses Fisher-Yates -- shuffle algorithm. -- -- Implementation details prevent a native implementation of the 'MWC.uniformShuffleM' -- function. Use the native API if this is required. uniformShuffle :: (Has Random sig m, Vector v a) => v a -> m (v a) uniformShuffle n = send (Random (Shuffle n))