Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Free Rather Than MonadPrompt #79

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions random-fu/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* Chnages in 0.3.0.0:

* Drop usage of `random-source` in favor of `random`

* Changes in 0.2.7.7: Update to random-1.2. Revert 0.2.7.6 changes (which added an extra constraint to `Data.Random.Sample.sampleState` and `Data.Random.Sample.sampleStateT`).

* Changes in 0.2.7.4: Compatibility with ghc 8.8.
Expand Down
19 changes: 9 additions & 10 deletions random-fu/random-fu.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: random-fu
version: 0.2.7.7
version: 1.0.0.0
stability: provisional

cabal-version: >= 1.10
Expand All @@ -12,21 +12,21 @@ homepage: https://github.com/mokus0/random-fu

category: Math
synopsis: Random number generation
description: Random number generation based on modeling random
description: Random number generation based on modeling random
variables in two complementary ways: first, by the
parameters of standard mathematical distributions and,
second, 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
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
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
a fair bit slower than straight C implementations of
the same algorithms.

tested-with: GHC == 7.10.3
Expand Down Expand Up @@ -83,25 +83,24 @@ Library
else
cpp-options: -Dold_Fixed
build-depends: base >= 4 && <4.2

if flag(mtl2)
build-depends: mtl == 2.*
cpp-options: -DMTL2
else
build-depends: mtl == 1.*

build-depends: math-functions,
monad-loops >= 0.3.0.1,
random >= 1.2 && < 1.3,
random-shuffle,
random-source == 0.3.*,
rvar == 0.2.*,
rvar >= 0.3,
syb,
template-haskell,
transformers,
vector >= 0.7,
erf

if impl(ghc == 7.2.1)
-- Doesn't work under GHC 7.2.1 due to
-- http://hackage.haskell.org/trac/ghc/ticket/5410
Expand Down
55 changes: 25 additions & 30 deletions random-fu/src/Data/Random.hs
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
-- |Flexible modeling and sampling of random variables.
--
-- The central abstraction in this library is the concept of a random
-- variable. It is not fully formalized in the standard measure-theoretic
-- language, but rather is informally defined as a \"thing you can get random
-- values out of\". Different random variables may have different types of
-- The central abstraction in this library is the concept of a random
-- variable. It is not fully formalized in the standard measure-theoretic
-- language, but rather is informally defined as a \"thing you can get random
-- values out of\". Different random variables may have different types of
-- values they can return or the same types but different probabilities for
-- each value they can return. The random values you get out of them are
-- traditionally called \"random variates\".
--
-- Most imperative-language random number libraries are all about obtaining
-- and manipulating random variates. This one is about defining, manipulating
-- and sampling random variables. Computationally, the distinction is small
-- and mostly just a matter of perspective, but from a program design
--
-- Most imperative-language random number libraries are all about obtaining
-- and manipulating random variates. This one is about defining, manipulating
-- and sampling random variables. Computationally, the distinction is small
-- and mostly just a matter of perspective, but from a program design
-- perspective it provides both a powerfully composable abstraction and a
-- very useful separation of concerns.
--
--
-- Abstract random variables as implemented by 'RVar' are composable. They can
-- be defined in a monadic / \"imperative\" style that amounts to manipulating
-- variates, but with strict type-level isolation. Concrete random variables
-- are also provided, but they do not compose as generically. The 'Distribution'
-- type class allows concrete random variables to \"forget\" their concreteness
-- so that they can be composed. For examples of both, see the documentation
-- for 'RVar' and 'Distribution', as well as the code for any of the concrete
-- type class allows concrete random variables to \"forget\" their concreteness
-- so that they can be composed. For examples of both, see the documentation
-- for 'RVar' and 'Distribution', as well as the code for any of the concrete
-- distributions such as 'Uniform', 'Gamma', etc.
--
--
-- Both abstract and concrete random variables can be sampled (despite the
-- types GHCi may list for the functions) by the functions in "Data.Random.Sample".
--
--
-- Random variable sampling is done with regard to a generic basis of primitive
-- random variables defined in "Data.Random.Internal.Primitives". This basis
-- random variables defined in "Data.Random.Internal.Primitives". This basis
-- is very low-level and the actual set of primitives is still fairly experimental,
-- which is why it is in the \"Internal\" sub-heirarchy. User-defined variables
-- should use the existing high-level variables such as 'Uniform' and 'Normal'
-- rather than these basis variables. "Data.Random.Source" defines classes for
-- entropy sources that provide implementations of these primitive variables.
-- entropy sources that provide implementations of these primitive variables.
-- Several implementations are available in the Data.Random.Source.* modules.
module Data.Random
( -- * Random variables
Expand All @@ -43,32 +43,26 @@ module Data.Random

-- ** Concrete ('Distribution')
Distribution(..), CDF(..), PDF(..),

-- * Sampling random variables
Sampleable(..), sample, sampleState, sampleStateT,
Sampleable(..), sample, sampleState, samplePure,

-- * A few very common distributions
Uniform(..), uniform, uniformT,
StdUniform(..), stdUniform, stdUniformT,
Normal(..), normal, stdNormal, normalT, stdNormalT,
Gamma(..), gamma, gammaT,

-- * Entropy Sources
MonadRandom, RandomSource, StdRandom(..),
StatefulGen, RandomGen,

-- * Useful list-based operations
randomElement,
shuffle, shuffleN, shuffleNofM

) where

import Data.Random.Sample
import Data.Random.Source (MonadRandom, RandomSource)
import Data.Random.Source.IO ()
import Data.Random.Source.MWC ()
import Data.Random.Source.StdGen ()
import Data.Random.Source.PureMT ()
import Data.Random.Source.Std
import Data.Random.Distribution
import Data.Random.Distribution.Gamma
import Data.Random.Distribution.Normal
Expand All @@ -78,3 +72,4 @@ import Data.Random.Lift ()
import Data.Random.List
import Data.Random.RVar

import System.Random.Stateful (StatefulGen, RandomGen)
2 changes: 1 addition & 1 deletion random-fu/src/Data/Random/Distribution.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class Distribution d t where
-- |Return a random variable with the given distribution, pre-lifted to an arbitrary 'RVarT'.
-- Any arbitrary 'RVar' can also be converted to an 'RVarT m' for an arbitrary 'm', using
-- either 'lift' or 'sample'.
rvarT :: d t -> RVarT n t
rvarT :: Functor n => d t -> RVarT n t
rvarT d = lift (rvar d)

-- FIXME: I am not sure about giving default instances
Expand Down
6 changes: 3 additions & 3 deletions random-fu/src/Data/Random/Distribution/Bernoulli.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ bernoulli p = rvar (Bernoulli p)
-- |Generate a Bernoulli process with the given probability. For @Bool@ results,
-- @bernoulli p@ will return True (p*100)% of the time and False otherwise.
-- For numerical types, True is replaced by 1 and False by 0.
bernoulliT :: Distribution (Bernoulli b) a => b -> RVarT m a
bernoulliT :: (Distribution (Bernoulli b) a, Functor m) => b -> RVarT m a
bernoulliT p = rvarT (Bernoulli p)

-- |A random variable whose value is 'True' the given fraction of the time
-- and 'False' the rest.
boolBernoulli :: (Fractional a, Ord a, Distribution StdUniform a) => a -> RVarT m Bool
boolBernoulli :: (Fractional a, Ord a, Distribution StdUniform a, Functor m) => a -> RVarT m Bool
boolBernoulli p = do
x <- stdUniformT
return (x <= p)
Expand All @@ -43,7 +43,7 @@ boolBernoulliCDF p False = (1 - realToFrac p)

-- | @generalBernoulli t f p@ generates a random variable whose value is @t@
-- with probability @p@ and @f@ with probability @1-p@.
generalBernoulli :: Distribution (Bernoulli b) Bool => a -> a -> b -> RVarT m a
generalBernoulli :: (Distribution (Bernoulli b) Bool, Functor m) => a -> a -> b -> RVarT m a
generalBernoulli f t p = do
x <- bernoulliT p
return (if x then t else f)
Expand Down
12 changes: 6 additions & 6 deletions random-fu/src/Data/Random/Distribution/Beta.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import Data.Random.Distribution.Uniform

import Numeric.SpecFunctions

{-# SPECIALIZE fractionalBeta :: Float -> Float -> RVarT m Float #-}
{-# SPECIALIZE fractionalBeta :: Double -> Double -> RVarT m Double #-}
fractionalBeta :: (Fractional a, Eq a, Distribution Gamma a, Distribution StdUniform a) => a -> a -> RVarT m a
{-# SPECIALIZE fractionalBeta :: Functor m => Float -> Float -> RVarT m Float #-}
{-# SPECIALIZE fractionalBeta :: Functor m => Double -> Double -> RVarT m Double #-}
fractionalBeta :: (Fractional a, Eq a, Distribution Gamma a, Distribution StdUniform a, Functor m) => a -> a -> RVarT m a
fractionalBeta 1 1 = stdUniformT
fractionalBeta a b = do
x <- gammaT a 1
Expand All @@ -32,9 +32,9 @@ fractionalBeta a b = do
beta :: Distribution Beta a => a -> a -> RVar a
beta a b = rvar (Beta a b)

{-# SPECIALIZE betaT :: Float -> Float -> RVarT m Float #-}
{-# SPECIALIZE betaT :: Double -> Double -> RVarT m Double #-}
betaT :: Distribution Beta a => a -> a -> RVarT m a
{-# SPECIALIZE betaT :: Functor m => Float -> Float -> RVarT m Float #-}
{-# SPECIALIZE betaT :: Functor m => Double -> Double -> RVarT m Double #-}
betaT :: (Distribution Beta a, Functor m) => a -> a -> RVarT m a
betaT a b = rvarT (Beta a b)

data Beta a = Beta a a
Expand Down
22 changes: 11 additions & 11 deletions random-fu/src/Data/Random/Distribution/Binomial.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import Numeric ( log1p )
-- note that although it's fast enough for large (eg, 2^10000)
-- @Integer@s, it's not accurate enough when using @Double@ as
-- the @b@ parameter.
integralBinomial :: (Integral a, Floating b, Ord b, Distribution Beta b, Distribution StdUniform b) => a -> b -> RVarT m a
integralBinomial :: (Integral a, Floating b, Ord b, Distribution Beta b, Distribution StdUniform b, Functor m) => a -> b -> RVarT m a
integralBinomial = bin 0
where
bin :: (Integral a, Floating b, Ord b, Distribution Beta b, Distribution StdUniform b) => a -> a -> b -> RVarT m a
bin :: (Integral a, Floating b, Ord b, Distribution Beta b, Distribution StdUniform b, Functor m) => a -> a -> b -> RVarT m a
bin !k !t !p
| t > 10 = do
let a = 1 + t `div` 2
Expand Down Expand Up @@ -118,15 +118,15 @@ floatingBinomialLogPDF t p x = logPdf (Binomial (truncate t :: Integer) p) (floo
binomial :: Distribution (Binomial b) a => a -> b -> RVar a
binomial t p = rvar (Binomial t p)

{-# SPECIALIZE binomialT :: Int -> Float -> RVarT m Int #-}
{-# SPECIALIZE binomialT :: Int -> Double -> RVarT m Int #-}
{-# SPECIALIZE binomialT :: Integer -> Float -> RVarT m Integer #-}
{-# SPECIALIZE binomialT :: Integer -> Double -> RVarT m Integer #-}
{-# SPECIALIZE binomialT :: Float -> Float -> RVarT m Float #-}
{-# SPECIALIZE binomialT :: Float -> Double -> RVarT m Float #-}
{-# SPECIALIZE binomialT :: Double -> Float -> RVarT m Double #-}
{-# SPECIALIZE binomialT :: Double -> Double -> RVarT m Double #-}
binomialT :: Distribution (Binomial b) a => a -> b -> RVarT m a
{-# SPECIALIZE binomialT :: Functor m => Int -> Float -> RVarT m Int #-}
{-# SPECIALIZE binomialT :: Functor m => Int -> Double -> RVarT m Int #-}
{-# SPECIALIZE binomialT :: Functor m => Integer -> Float -> RVarT m Integer #-}
{-# SPECIALIZE binomialT :: Functor m => Integer -> Double -> RVarT m Integer #-}
{-# SPECIALIZE binomialT :: Functor m => Float -> Float -> RVarT m Float #-}
{-# SPECIALIZE binomialT :: Functor m => Float -> Double -> RVarT m Float #-}
{-# SPECIALIZE binomialT :: Functor m => Double -> Float -> RVarT m Double #-}
{-# SPECIALIZE binomialT :: Functor m => Double -> Double -> RVarT m Double #-}
binomialT :: (Distribution (Binomial b) a, Functor m) => a -> b -> RVarT m a
binomialT t p = rvarT (Binomial t p)

data Binomial b a = Binomial a b
Expand Down
6 changes: 2 additions & 4 deletions random-fu/src/Data/Random/Distribution/Categorical.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ import Data.Random.Distribution.Uniform
import Control.Arrow
import Control.Monad
import Control.Monad.ST
import Data.Foldable (Foldable(foldMap))
import Data.STRef
import Data.Traversable (Traversable(traverse, sequenceA))

import Data.List
import Data.Function
Expand All @@ -39,7 +37,7 @@ categorical = rvar . fromList

-- |Construct a 'Categorical' random process from a list of probabilities
-- and categories, where the probabilities all sum to 1.
categoricalT :: (Num p, Distribution (Categorical p) a) => [(p,a)] -> RVarT m a
categoricalT :: (Num p, Distribution (Categorical p) a, Functor m) => [(p,a)] -> RVarT m a
categoricalT = rvarT . fromList

-- |Construct a 'Categorical' random variable from a list of weights
Expand All @@ -49,7 +47,7 @@ weightedCategorical = rvar . fromWeightedList

-- |Construct a 'Categorical' random process from a list of weights
-- and categories. The weights do /not/ have to sum to 1.
weightedCategoricalT :: (Fractional p, Eq p, Distribution (Categorical p) a) => [(p,a)] -> RVarT m a
weightedCategoricalT :: (Fractional p, Eq p, Distribution (Categorical p) a, Functor m) => [(p,a)] -> RVarT m a
weightedCategoricalT = rvarT . fromWeightedList

-- | Construct a 'Categorical' distribution from a list of weighted categories.
Expand Down
2 changes: 1 addition & 1 deletion random-fu/src/Data/Random/Distribution/ChiSquare.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Numeric.SpecFunctions
chiSquare :: Distribution ChiSquare t => Integer -> RVar t
chiSquare = rvar . ChiSquare

chiSquareT :: Distribution ChiSquare t => Integer -> RVarT m t
chiSquareT :: (Distribution ChiSquare t, Functor m) => Integer -> RVarT m t
chiSquareT = rvarT . ChiSquare

newtype ChiSquare b = ChiSquare Integer
Expand Down
4 changes: 2 additions & 2 deletions random-fu/src/Data/Random/Distribution/Dirichlet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Data.Random.Distribution.Gamma

import Data.List

fractionalDirichlet :: (Fractional a, Distribution Gamma a) => [a] -> RVarT m [a]
fractionalDirichlet :: (Fractional a, Distribution Gamma a, Functor m) => [a] -> RVarT m [a]
fractionalDirichlet [] = return []
fractionalDirichlet [_] = return [1]
fractionalDirichlet as = do
Expand All @@ -24,7 +24,7 @@ fractionalDirichlet as = do
dirichlet :: Distribution Dirichlet [a] => [a] -> RVar [a]
dirichlet as = rvar (Dirichlet as)

dirichletT :: Distribution Dirichlet [a] => [a] -> RVarT m [a]
dirichletT :: (Distribution Dirichlet [a], Functor m) => [a] -> RVarT m [a]
dirichletT as = rvarT (Dirichlet as)

newtype Dirichlet a = Dirichlet a deriving (Eq, Show)
Expand Down
4 changes: 2 additions & 2 deletions random-fu/src/Data/Random/Distribution/Exponential.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ See also 'exponential'.
-}
newtype Exponential a = Exp a

floatingExponential :: (Floating a, Distribution StdUniform a) => a -> RVarT m a
floatingExponential :: (Floating a, Distribution StdUniform a, Functor m) => a -> RVarT m a
floatingExponential lambdaRecip = do
x <- stdUniformT
return (negate (log x) * lambdaRecip)
Expand All @@ -48,7 +48,7 @@ A random variable transformer which samples from the exponential distribution.
alternatively be viewed as an exponential random variable with parameter @lambda
= 1 / mu@.
-}
exponentialT :: Distribution Exponential a => a -> RVarT m a
exponentialT :: (Distribution Exponential a, Functor m) => a -> RVarT m a
exponentialT = rvarT . Exp

instance (Floating a, Distribution StdUniform a) => Distribution Exponential a where
Expand Down
10 changes: 5 additions & 5 deletions random-fu/src/Data/Random/Distribution/Gamma.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ import Numeric.SpecFunctions

-- |derived from Marsaglia & Tang, "A Simple Method for generating gamma
-- variables", ACM Transactions on Mathematical Software, Vol 26, No 3 (2000), p363-372.
{-# SPECIALIZE mtGamma :: Double -> Double -> RVarT m Double #-}
{-# SPECIALIZE mtGamma :: Float -> Float -> RVarT m Float #-}
{-# SPECIALIZE mtGamma :: Functor m => Double -> Double -> RVarT m Double #-}
{-# SPECIALIZE mtGamma :: Functor m => Float -> Float -> RVarT m Float #-}
mtGamma
:: (Floating a, Ord a,
Distribution StdUniform a,
Distribution Normal a)
Distribution Normal a, Functor m)
=> a -> a -> RVarT m a
mtGamma a b
| a < 1 = do
Expand Down Expand Up @@ -64,13 +64,13 @@ mtGamma a b
gamma :: (Distribution Gamma a) => a -> a -> RVar a
gamma a b = rvar (Gamma a b)

gammaT :: (Distribution Gamma a) => a -> a -> RVarT m a
gammaT :: (Distribution Gamma a, Functor m) => a -> a -> RVarT m a
gammaT a b = rvarT (Gamma a b)

erlang :: (Distribution (Erlang a) b) => a -> RVar b
erlang a = rvar (Erlang a)

erlangT :: (Distribution (Erlang a) b) => a -> RVarT m b
erlangT :: (Distribution (Erlang a) b, Functor m) => a -> RVarT m b
erlangT a = rvarT (Erlang a)

data Gamma a = Gamma a a
Expand Down
2 changes: 1 addition & 1 deletion random-fu/src/Data/Random/Distribution/Multinomial.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Data.Random.Distribution.Binomial
multinomial :: Distribution (Multinomial p) [a] => [p] -> a -> RVar [a]
multinomial ps n = rvar (Multinomial ps n)

multinomialT :: Distribution (Multinomial p) [a] => [p] -> a -> RVarT m [a]
multinomialT :: (Distribution (Multinomial p) [a], Functor m) => [p] -> a -> RVarT m [a]
multinomialT ps n = rvarT (Multinomial ps n)

data Multinomial p a where
Expand Down
Loading