packages feed

probable (empty) → 0.1.0.0

raw patch · 13 files changed

+1751/−0 lines, 13 filesdep +basedep +criteriondep +mtlsetup-changed

Dependencies added: base, criterion, mtl, mwc-random, mwc-random-monad, primitive, probable, statistics, transformers, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Alp Mestanogullari++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Alp Mestanogullari nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,89 @@+probable+========++Simple random value generation for haskell, using an efficient+random generator and minimizing system calls. But the library also+lets you work with distributions over a finite set, adapting+code from Eric Kidd's posts, and all the usual distributions+covered in the [statistics](http://hackage.haskell.org/package/statistics)+package.++You can see how it looks in [examples](https://github.com/alpmestan/probable/tree/master/examples), or below. You can view the documentation for 0.1 [here](http://alpmestan.com/probable/).++## Example++Simple example of random generation for your types, using _probable_.++``` haskell+module Main where++import Control.Applicative+import Control.Monad+import Math.Probable++import qualified Data.Vector.Unboxed as VU++data Person = Person +    { age    :: Int+    , weight :: Double+    , salary :: Int+    } deriving (Eq, Show)++person :: RandT IO Person+person = +    Person <$> intIn (1, 100)+           <*> doubleIn (2, 130)+           <*> intIn (500, 10000)++randomPersons :: Int -> IO [Person]+randomPersons n = mwc $ listOf n person++randomDoubles :: Int -> IO (VU.Vector Double)+randomDoubles n = mwc $ vectorOf n double++main :: IO ()+main = do+	randomPersons 10 >>= mapM_ print+	randomDoubles 10 >>= VU.mapM_ print+```++Distributions over finite sets, conditional probabilities and random sampling.++``` haskell+module Main where++import Math.Probable++import qualified Data.Vector as V++data Book = Interesting +		  | Boring+	deriving (Eq, Show)++bookPrior :: Finite d => d Book+bookPrior = weighted [ (Interesting, 0.2) +					 , (Boring, 0.8) +					 ]++twoBooks :: Finite d => d (Book, Book)+twoBooks = do+	book1 <- bookPrior+	book2 <- bookPrior+	return (book1, book2)++sampleBooks :: RandT IO (V.Vector Book)+sampleBooks = vectorOf 10 bookPrior++oneInteresting :: Fin (Book, Book)+oneInteresting = bayes $ do+	(b1, b2) <- twoBooks+	condition (b1 == Interesting || b2 == Interesting)+	return (b1, b2)++main :: IO ()+main = do+	print $ exact bookPrior+	mwc sampleBooks >>= print+	print $ exact twoBooks+	print $ exact oneInteresting+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/distrib.hs view
@@ -0,0 +1,55 @@+module Main where++import Control.Applicative+import Control.Monad+import Control.Monad.Primitive+import Criterion.Main+import System.Random.MWC++import qualified Data.Vector         as V+import qualified Data.Vector.Unboxed as U+import Math.Probable++n :: Int+n = 100000++data Fruit = Apple | Banana | Orange+    deriving (Eq, Show)++fruitDist :: FromFinite d => d Fruit+fruitDist = weighted [ (Apple, 0.3)+                     , (Banana, 0.6)+                     , (Orange, 0.1)+                     ]+liftF' :: Fin a -> IO a+liftF' dist = do+    d <- withSystemRandom . asGenIO $ uniform+    pick (P d) (exact dist)++instance FromFinite IO where+    weighted = liftF' . weighted++observe1 :: RandT IO Fruit -> IO (V.Vector Fruit)+observe1 = mwc . vectorOf5 n++observe2 :: RandT IO Fruit -> IO (V.Vector Fruit)+observe2 r = mwc . RandT $ \gen ->+   V.replicateM n (runRandT r gen)++observe4 :: IO Fruit+         -> IO (V.Vector Fruit)+observe4 fr = +    V.replicateM n (fr gen)+++-- | Time to benchmark!+main :: IO ()+main = do +    defaultMain +        [ +            bgroup "big vector of fruits"+                [ bench "observe1" $ whnfIO (observe1 fruitDist)+                , bench "observe2" $ whnfIO (observe2 fruitDist)+                , bench "observe3" $ whnfIO (observe4 fruitDist)+                ]+        ]
+ bench/random.hs view
@@ -0,0 +1,88 @@+module Main where++import Control.Applicative+import Control.Monad+import Criterion.Main+import System.Random.MWC++import qualified System.Random.MWC.Monad as MWCMonad++import qualified Data.Vector         as V+import qualified Data.Vector.Unboxed as U+import Math.Probable++probable3 :: (U.Unbox a, Variate a) => Int -> IO (U.Vector a)+probable3 n = mwc (vectorOf3 n)+{-# INLINE probable3 #-}++probable5 :: U.Unbox a => Int -> RandT IO a -> IO (U.Vector a)+probable5 n gen = mwc (vectorOf5 n gen)+{-# INLINE probable5 #-}++mwc1 :: (U.Unbox a, Variate a) => Int -> IO (U.Vector a)+mwc1 n = +  withSystemRandom . asGenIO $ +      \gen -> uniformVector gen n+{-# INLINE mwc1 #-}++mwc2 :: (U.Unbox a, Variate a) => Int -> IO (U.Vector a)+mwc2 n =+  withSystemRandom . asGenIO $+      \gen -> U.replicateM n (uniform gen)+{-# INLINE mwc2 #-}++mwcm :: (U.Unbox a, Variate a) => Int -> IO (U.Vector a)+mwcm n = +  MWCMonad.runWithSystemRandom . MWCMonad.asRandIO $+      MWCMonad.toRand (flip uniformVector n)+{-# INLINE mwcm #-}++{-+-- | Dummy 'Person' type+data Person = Person +    { age    :: Int+    , weight :: Double+    , salary :: Int+    } deriving (Eq, Show)++person :: (Generator g m Double, Generator g m Int) +       => RandT g m Person+person = +    Person <$> sampleUniform (1, 100)+           <*> sampleUniform (2, 130)+           <*> sampleUniform (500, 10000)++randomPersons :: Int -> IO (V.Vector Person)+randomPersons n = mwc (vectorOf n person)+-}++n :: Int+n = 10000000++-- | Time to benchmark!+main :: IO ()+main = do +    defaultMain +        [ +            bgroup "big vector of int"+                [ bench "probable3" $ whnfIO (i $ probable3 n)+                , bench "probable5" $ whnfIO (probable5 n int)+                , bench "mwc-random" $ whnfIO (i $ mwc1 n)+                , bench "mwc-random2" $ whnfIO (i $ mwc2 n)+                , bench "mwc-random-monad2" $ whnfIO (i $ mwcm n)+                ],++            bgroup "big vector of double"+                [ bench "probable3" $ whnfIO (d $ probable3 n)+                , bench "probable5" $ whnfIO (probable5 n double)+                , bench "mwc-random" $ whnfIO (d $ mwc1 n)+                , bench "mwc-random2" $ whnfIO (d $ mwc2 n)+                , bench "mwc-random-monad2" $ whnfIO (d $ mwcm n)+                ]+        ]++d :: IO (U.Vector Double) -> IO (U.Vector Double)+d = id++i :: IO (U.Vector Int) -> IO (U.Vector Int)+i = id
+ examples/finite.hs view
@@ -0,0 +1,36 @@+module Main where++import Math.Probable++import qualified Data.Vector as V++data Book = Interesting +		  | Boring+	deriving (Eq, Show)++bookPrior :: Finite d => d Book+bookPrior = weighted [ (Interesting, 0.2) +					 , (Boring, 0.8) +					 ]++twoBooks :: Finite d => d (Book, Book)+twoBooks = do+	book1 <- bookPrior+	book2 <- bookPrior+	return (book1, book2)++sampleBooks :: RandT IO (V.Vector Book)+sampleBooks = vectorOf 10 bookPrior++oneInteresting :: Fin (Book, Book)+oneInteresting = bayes $ do+	(b1, b2) <- twoBooks+	condition (b1 == Interesting || b2 == Interesting)+	return (b1, b2)++main :: IO ()+main = do+	print $ exact bookPrior+	mwc sampleBooks >>= print+	print $ exact twoBooks+	print $ exact oneInteresting
+ examples/montyhall.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE BangPatterns #-}+module Main where++import Data.List+import Math.Probable++-- | We have 3 distinct doors+data Door = D1 | D2 | D3+    deriving (Eq, Show)++-- | Our 3 doors in a list+doors :: [Door]+doors = [D1, D2, D3]++-- | We can either Win or Lose+data Result = Win | Lose+    deriving (Eq, Show)++-- | Knowing the door we have chosen so far+--   and the one that's been opened+--   we decide to keep the one we've picked+keep :: Door -> Door -> Door+keep chosen _ = chosen++-- | Knowing the door we have chosen so far+--   and the one that's been opened+--   we decide to switch to the third one+switch :: Door -> Door -> Door+switch chosen opened = head $ doors \\ [chosen, opened]++-- | Given one of the two functions above ("strategies"),+--   and (the chosen door, the already opened one, and +--   the one with the car)+--   we check wether we won or not+resultOf :: (Door -> Door -> Door) -> Door -> Door -> Door -> Result+resultOf strategy chosen opened cardoor =+    case strategy chosen opened == cardoor of+        True  -> Win+        False -> Lose++-- | We run the distribution of results and group the 'Win's and the 'Lose's+--   respective probabilities together+--   maybe this should be in the library, with a more general type...+collect :: Fin Result -> (Event Result, Event Result)+collect = f . exact+    where f = toPair . foldl' combine (0, 0)+          combine (!winP, !loseP) (Event Win p)  = (winP+p, loseP)+          combine (!winP, !loseP) (Event Lose p) = (winP,   loseP+p)+          toPair (winP, loseP) = (Event Win winP, Event Lose loseP)++-- | Given a strategy to adopt, what's the distribution of Win/Lose ?+result :: (Door -> Door -> Door)+       -> Fin Result+result strategy = do+    -- we pick a door uniformly for hiding the car+    carDoor    <- uniformly doors++    -- we pick a door uniformly for the player+    chosenDoor <- uniformly doors++    -- we open a door that neither hides the car (for suspense)+    -- nor the one the player has picked (but they can be the same door)+    openedDoor <- uniformly $ doors \\ [carDoor, chosenDoor]++    -- ok, the player tells us whether he decides to keep or switch+    -- we now check whether he wins or not+    let res = resultOf strategy chosenDoor openedDoor carDoor++    -- we return the result, to make this a distribution of results+    return res++-- | Given a strategy to adopt, distribution of doors for a Win ?+--   this uses Bayes' rule, and consequently lives in 'FinBayes'+result' :: (Door -> Door -> Door)+        -> FinBayes (Door, Door, Door)+result' strategy = do+    -- we pick a door uniformly for hiding the car+    carDoor    <- uniformly doors++    -- we pick a door uniformly for the player+    chosenDoor <- uniformly doors++    -- we open a door that neither hides the car (for suspense)+    -- nor the one the player has picked (but they can be the same door)+    openedDoor <- uniformly $ doors \\ [carDoor, chosenDoor]++    -- ok, the player tells us whether he decides to keep or switch+    -- we now check whether he wins or not+    let res = resultOf strategy chosenDoor openedDoor carDoor++    -- here we discard all the combinations that don't lead to Win+    condition (res == Win)++    -- and return the combination+    return (chosenDoor, openedDoor, carDoor)++main :: IO ()+main = do+    putStrLn $ "Using the conservative strategy: "+            ++ show (collect $ result keep)+    -- Using the conservative strategy: (Event Win 33.3%,Event Lose 66.7%)++    putStrLn $ "Switching: "+            ++ show (collect $ result switch)+    -- Switching: (Event Win 66.7%,Event Lose 33.3%)++    putStrLn "---"++    putStrLn $ "Winning (initial door, opened door, car door)'s - CONSERVATIVE:"+    mapM_ print . exact . bayes $ result' keep+    -- Event (D1,D2,D1) 16.7%+    -- Event (D1,D3,D1) 16.7%+    -- Event (D2,D1,D2) 16.7%+    -- Event (D2,D3,D2) 16.7%+    -- Event (D3,D1,D3) 16.7%+    -- Event (D3,D2,D3) 16.7%++    putStrLn "---"++    putStrLn $ "Winning (initial door, opened door, car door)'s - SWITCHING:"+    mapM_ print . exact . bayes $ result' switch+    -- Event (D2,D3,D1) 16.7%+    -- Event (D3,D2,D1) 16.7%+    -- Event (D1,D3,D2) 16.7%+    -- Event (D3,D1,D2) 16.7%+    -- Event (D1,D2,D3) 16.7%+    -- Event (D2,D1,D3) 16.7%
+ examples/simple.hs view
@@ -0,0 +1,30 @@+module Main where++import Control.Applicative+import Control.Monad+import Math.Probable++import qualified Data.Vector.Unboxed as VU++data Person = Person +    { age    :: Int+    , weight :: Double+    , salary :: Int+    } deriving (Eq, Show)++person :: RandT IO Person+person = +    Person <$> uniformIn (1, 100)+           <*> uniformIn (2, 130)+           <*> uniformIn (500, 10000)++randomPersons :: Int -> IO [Person]+randomPersons n = mwc $ listOf n person++randomDoubles :: Int -> IO (VU.Vector Double)+randomDoubles n = mwc $ vectorOf n double++main :: IO ()+main = do+	randomPersons 10 >>= mapM_ print+	randomDoubles 10 >>= VU.mapM_ print
+ probable.cabal view
@@ -0,0 +1,121 @@+name:                probable+version:             0.1.0.0+synopsis:            Easy and reasonably efficient probabilistic programming and random generation+description:         Easy and reasonably efficient probabilistic programming and random generation+                     .+                     This library gives a common language to speak about +                     probability distributions and+                     random generation, by wrapping both, when necessary,+                     in a 'RandT' monad defined in @Math.Probable.Random@.+                     This module also provides a lot of useful little+                     combinators for easily describing how random values for your+                     types should be generated. +                     .+                     In @Math.Probable.Distribution@, you'll find functions for generating+                     random values that follow any distribution supported by +                     <http://hackage.haskell.org/package/mwc-random mwc-random>.+                     .+                     In @Math.Probable.Distribution.Finite@, you'll find an adaptation+                     of Eric Kidd's work on probability monads (from +                     <http://www.randomhacks.net/probability-monads/ here>).+                     .+                     You may want to check the examples bundled with this package,+                     viewable online at <https://github.com/alpmestan/probable/tree/master/examples>.+                     One of these examples is simple enough to be worth reproducing here.+                     .+                     > module Main where+                     >+                     > import Control.Applicative+                     > import Control.Monad+                     > import Math.Probable+                     >+                     > import qualified Data.Vector.Unboxed as VU+                     > +                     > data Person = Person Int    -- ^ age+                     >                      Double -- ^ weight (kgs)+                     >                      Double -- ^ salary (e.g euros)+                     >     deriving (Eq, Show)+                     >+                     > person :: RandT IO Person+                     > person = +                     >     Person <$> uniformIn (1, 100)+                     >            <*> uniformIn (2, 130)+                     >            <*> uniformIn (500, 10000)+                     >+                     > randomPersons :: Int -> IO [Person]+                     > randomPersons n = mwc $ listOf n person+                     > +                     > randomDoubles :: Int -> IO (VU.Vector Double)+                     > randomDoubles n = mwc $ vectorOf n double+                     > +                     > main :: IO ()+                     > main = do+                     >     randomPersons 10 >>= mapM_ print+                     >     randomDoubles 10 >>= VU.mapM_ print+                     .+                     Please report any feature request or problem, either by email+                     or through github's issues/feature requests.+homepage:            http://github.com/alpmestan/probable+bug-reports:         http://github.com/alpmestan/probable/issues+license:             BSD3+license-file:        LICENSE+author:              Alp Mestanogullari+maintainer:          alpmestan@gmail.com+copyright:           2014 Alp Mestanogullari+category:            Math, Statistics+build-type:          Simple +cabal-version:       >=1.10+tested-with:         GHC == 7.6.3, GHC == 7.8.2+extra-source-files:  bench/*.hs,+                     examples/*.hs,+                     README.md+++source-repository head+  type:     git+  location: https://github.com/alpmestan/probable.git++library+  exposed-modules:     Math.Probable,+                       Math.Probable.Distribution,+                       Math.Probable.Distribution.Finite,+                       Math.Probable.Random+  other-modules:       +  build-depends:       base >=4.5 && <4.8,+                       statistics >= 0.10,+                       vector >= 0.10,+                       mwc-random >= 0.10,+                       primitive,+                       mtl,+                       transformers >= 0.3+                       +  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options:         -O2 -funbox-strict-fields -Wall++benchmark random+  main-is:           random.hs+  hs-source-dirs:    bench+  ghc-options:       -O2 -funbox-strict-fields+  type:              exitcode-stdio-1.0+  build-depends:     base >= 4 && < 5, +                     vector >= 0.7, +                     criterion, +                     probable,+                     mwc-random,+                     mwc-random-monad+  default-language:  Haskell2010++benchmark distrib+  main-is:           distrib.hs+  hs-source-dirs:    bench+  ghc-options:       -O2 -funbox-strict-fields+  type:              exitcode-stdio-1.0+  build-depends:     base >= 4 && < 5, +                     vector >= 0.7, +                     criterion, +                     probable,+                     mwc-random,+                     primitive+                     +  default-language:  Haskell2010
+ src/Math/Probable.hs view
@@ -0,0 +1,23 @@+-- |+-- Module       : Math.Probable+-- Copyright    : (c) 2014 Alp Mestanogullari+-- License      : BSD3+-- Maintainer   : alpmestan@gmail.com+-- Stability    : experimental+-- Portability  : GHC+-- +-- Easy, composable and efficient random number generation,+-- with support for distributions over a finite set +-- and your usual probability distributions.++module Math.Probable +       ( -- * random value generation+         module Math.Probable.Random++       , -- * distributions+         module Math.Probable.Distribution+       ) where++import Math.Probable.Distribution+import Math.Probable.Random+
+ src/Math/Probable/Distribution.hs view
@@ -0,0 +1,282 @@+module Math.Probable.Distribution +    ( -- * Common distributions+      beta+    , cauchy+    , cauchyStd+    , chiSquared+    , fisher+    , gamma+    , improperGamma+    , geometric+    , geometric0+    , student+    , uniform+    , normal+    , standard+    , normalFromSample+    , exponential+    , exponentialFromSample++     -- * Finite distributions +   , module Math.Probable.Distribution.Finite++     -- * Utility functions+   , continuous+   , discrete++   ) where++import Control.Monad.Primitive+import Math.Probable.Distribution.Finite+import Math.Probable.Random++import Statistics.Distribution (ContGen, DiscreteGen, genContVar, genDiscreteVar)+import Statistics.Distribution.Beta+import Statistics.Distribution.CauchyLorentz+import qualified Statistics.Distribution.ChiSquared as Chi+import qualified Statistics.Distribution.Exponential as E+import Statistics.Distribution.FDistribution+import Statistics.Distribution.Gamma+import qualified Statistics.Distribution.Geometric as G+import qualified Statistics.Distribution.Normal as N+import Statistics.Distribution.StudentT+import Statistics.Distribution.Uniform+import Statistics.Types (Sample)++-- | Sample from a continuous distribution from the 'statistics' package+-- +-- > λ> import qualified Statistics.Distribution.Normal as Normal+-- > λ> mwc $ continuous (Normal.normalDistr 0 1)+-- > -0.7266583064693862+--+-- This is equivalent to using 'normal' from this module.+continuous :: (ContGen d, PrimMonad m) +           => d -- ^ the continuous distribution to sample from+           -> RandT m Double+continuous d = RandT $ genContVar d+{-# INLINE continuous #-}++-- | Sample from a discrete distribution from the 'statistics' package+-- +-- > λ> import qualified Statistics.Distribution.Normal as Normal+-- > λ> mwc $ discrete (Geo.geometric 0.6)+-- > 2+--+-- This is equivalent to using 'geometric' from this module.+discrete :: (DiscreteGen d, PrimMonad m)+          => d -- ^ the discrete distribution to sample from+          -> RandT m Int+discrete d = RandT $ genDiscreteVar d++-- | Beta distribution (from @Statistics.Distribution.Beta@)+--+-- > λ> mwc $ listOf 10 (beta 81 219)+-- > [ 0.23238372272745833,0.252972980515086,0.22708315774257903+-- > , 0.25807200295967214,0.29794072226119983,0.24534701159196015+-- > , 0.24766870269839578,0.2994199351220346,0.2728157476212405,0.2593318159573564+-- > ]+beta :: PrimMonad m +     => Double -- ^ shape parameter alpha+     -> Double -- ^ shape parameter beta+     -> RandT m Double+beta alpha bet = continuous $ betaDistr alpha bet++-- | Cauchy distribution (from @Statistics.Distribution.Cauchy@)+-- +-- > λ> mwc $ listOf 10 (cauchy 0 0.1)+-- > [ -0.3932758718373347,0.490467375093784,4.2620417667423555e-2+-- > , 3.370509874905657e-2,-8.186484692937862e-2,9.371858212168262e-2+-- > , -1.1095818809115384e-2,3.0353983716155386e-2,0.22759697862410477+-- > , -0.1881828277028582 ]+cauchy :: PrimMonad m+       => Double -- ^ central point+       -> Double -- ^ scale parameter+       -> RandT m Double+cauchy p l = continuous $ cauchyDistribution p l++-- | Cauchy distribution around 0, with scale 1 (from @Statistics.Distribution.Cauchy@)+-- +-- > λ> mwc $ listOf 10 cauchyStd+-- > [ 9.409701589649838,-7.361963972107541,0.168746305673769+-- > , 5.091825420838711,-0.326080163135388,-1.2989850787629456+-- > , -2.685658063444485,0.22671438734899435,-1.602349559644217e-2+-- > , -0.6476292643908057 ]+cauchyStd :: PrimMonad m+          => RandT m Double+cauchyStd = cauchy 0 1++-- | Chi-squared distribution (from @Statistics.Distribution.ChiSquared@)+-- +-- > λ> mwc $ listOf 10 (chiSquare 4)+-- > [ 8.068852054279787,1.861584389294606,6.3049415103095265+-- > , 1.0512164068833838,1.6243237867165086,5.284901049954076+-- > , 0.4773242487947021,1.1753876666374887,5.21554771873363+-- > , 3.477574639460651 ]+chiSquared :: PrimMonad m+           => Int -- ^ number of degrees of freedom+           -> RandT m Double+chiSquared = continuous . Chi.chiSquared++-- | Fisher's F-Distribution (from @Statistics.Distribution.FDistribution@)+--+-- > λ> mwc $ listOf 10 (fisher 4 3)+-- > [ 3.437898578540642,0.844120450719367,1.9907851466347173+-- > , 2.0089975147012784,1.3729208790549117,0.9380430357924707+-- > , 2.642389323945247,1.0918121624055352,0.45650856735477335+-- > , 2.5134537326659196 ]+fisher :: PrimMonad m+       => Int+       -> Int+       -> RandT m Double+fisher a b = continuous $ fDistribution a b++-- | Gamma distribution (from @Statistics.Distribution.Gamma@)+-- +-- > λ> mwc $ listOf 10 (gamma 3 0.1)+-- > [ 5.683745415884202e-2,0.20726188766138176,0.3150672538487696+-- > , 0.4250825346490057,0.5586516230326105,0.46897413151474315+-- > , 0.18374916962208182,9.93000480494153e-2,0.6057279704154832+-- > , 0.11070190282993911 ]+gamma :: PrimMonad m+      => Double -- ^ shape parameter k+      -> Double -- ^ scale parameter theta+      -> RandT m Double+gamma k theta = continuous $ gammaDistr k theta++-- | Gamma distribution, without checking whether the parameter are valid+-- (from @Statistics.Distribution.Gamma@)+-- +-- > λ> mwc $ listOf 10 (improperGamma 3 0.1)+-- > [ 0.30431838005485,0.4044380297376584,2.8950141419406657e-2+-- > , 0.468271612850147,0.18587792578128381,0.22735854572527045+-- > , 0.5168050216325927,5.896911236207261e-2,0.24654560998405564+-- > , 0.10557650513145429 ]+improperGamma :: PrimMonad m+              => Double -- ^ shape parameter k+              -> Double -- ^ scale parameter theta+              -> RandT m Double+improperGamma k theta = continuous $ improperGammaDistr k theta++-- | Geometric distribution.+-- +-- Distribution of the number of trials needed to get one success.+-- See @Statistics.Distribution.Geometric@+--+-- > λ> mwc $ listOf 10 (geometric 0.8)+-- > [2,1,1,1,1,1,1,2,1,5]+geometric :: PrimMonad m+          => Double -- ^ success rate+          -> RandT m Int+geometric = discrete . G.geometric++-- | Geometric distribution.+--+-- Distribution of the number of failures before getting one success.+-- See @Statistics.Distribution.Geometric@+-- +-- > λ> mwc $ listOf 10 (geometric0 0.8)+-- > [0,0,0,0,0,1,1,0,0,0]+geometric0 :: PrimMonad m+           => Double+           -> RandT m Int+geometric0 = discrete . G.geometric0++-- | Student-T distribution (from @Statistics.Distribution.StudentT@)+-- +-- > λ> mwc $ listOf 10 (student 0.2)+-- > [ -14.221373473810829,-29.395749168822267,19.448665112984997+-- > , -30.00446058929083,-0.5033202547957609,2.321975597874013+-- > , 0.7884787761643617,-0.1895113832448149,-131.12901170537924+-- > , 1.371956948317759 ]+student :: PrimMonad m+        => Double +        -> RandT m Double+student = continuous . studentT++-- | Uniform distribution between 'a' and 'b' (from @Statistics.Distribution.Uniform@)+--+-- > λ> mwc $ listOf 10 (uniform 0.1 0.2)+-- > [ 0.1711914559256124,0.1275212181343327,0.15347702635758945+-- > , 0.1743662387063698,0.12047749686635312,0.10719840237585587+-- > , 0.10543681342025846,0.13482973080648325,0.19779298960413577+-- > , 0.1681037592576508 ]+uniform :: PrimMonad m+        => Double+        -> Double+        -> RandT m Double+uniform a b = continuous $ uniformDistr a b++-- | Normal distribution (from @Statistics.Distribution.Normal@)+--+-- > λ> mwc $ listOf 10 (normal 4 1)+-- > [ 3.6815394812555144,3.5958531529526727,3.775960990625964+-- > , 4.413109650155896,4.825826384709198,4.805629590118984+-- > , 5.259267547365003,4.45410634165052,4.886537243027636+-- > , 3.0409409067356954 ]+normal :: PrimMonad m+       => Double -- ^ mean+       -> Double -- ^ standard deviation+       -> RandT m Double+normal mean stddev = continuous $ N.normalDistr mean stddev++-- | The standard normal distribution (mean = 0, stddev = 1) (from @Statistics.Distribution.Normal@)+-- +-- > λ> mwc $ listOf 10 standard+-- > [ 0.2252627935262769,1.1831885443897947,-0.6577353418647461+-- > , 2.1574536855051853,-0.16983072710637676,0.9667954287638821+-- > , -1.8758605246293683,-0.8578048838241616,1.9516838769731923+-- > , 0.43752574431460434 ]+standard :: PrimMonad m+         => RandT m Double+standard = continuous N.standard++-- | Create a normal distribution using parameters estimated from the sample+-- (from @Statistics.Distribution.Normal@)+--+-- > λ> mwc . listOf 10 $ +-- >      normalFromSample $ +-- >        V.fromList [1,1,1,3,3,3,4+-- >                   ,4,4,4,4,4,4,4+-- >                   ,4,4,4,4,4,5,5+-- >                   ,5,7,7,7]+-- > [ 7.1837511677441395,2.388433817342809,5.252282321156134+-- > , 4.988163140851522,0.40102386713467864,4.4840751065620665+-- > , 2.1471370686776874,2.6591948802201046,3.843667372514598+-- > , 1.7650436484843248 ]+normalFromSample :: PrimMonad m+                 => Sample -- ^ sample+                 -> RandT m Double+normalFromSample = continuous . N.normalFromSample++-- | Exponential distribution (from @Statistics.Distribution.Exponential@)+-- +-- > λ> mwc $ listOf 10 (exponential 0.2)+-- > [ 5.713524665694821,1.7774315204594584,2.434017573227628+-- > , 5.463202731505528,0.5403008025455847,14.346316301765576+-- > , 7.380393612391503,24.800854500680032,0.8731076703020924+-- > , 6.1661076502236645 ]+exponential :: PrimMonad m+            => Double -- ^ lambda (scale) parameter+            -> RandT m Double+exponential = continuous . E.exponential++-- | Exponential distribution given a sample (from @Statistics.Distribution.Exponential@)+-- +-- > λ> mwc $ listOf 10 (exponentialFromSample $ V.fromList [1,1,1,0])+-- > [ 0.4237050903604833,1.934301502525168,0.7435728843566659+-- > , 1.8720263209574293,0.605750265970631,0.24103955067365979+-- > , 0.6294952762436511,1.660404952631443,0.6448230847113577+-- > , 0.8891555734786789 ]+exponentialFromSample :: PrimMonad m+                      => Sample+                      -> RandT m Double+exponentialFromSample = continuous . E.exponentialFromSample+++++++++
+ src/Math/Probable/Distribution/Finite.hs view
@@ -0,0 +1,385 @@+{-# LANGUAGE GeneralizedNewtypeDeriving, +             BangPatterns, +             TupleSections,+             FlexibleInstances, +             TypeSynonymInstances #-}+-- |+-- Module       : Math.Probable+-- License      : BSD3+-- Maintainer   : alpmestan@gmail.com+-- Stability    : experimental+-- Portability  : GHC+-- +-- /Fun with finite distributions!/+--+-- This all pretty much comes from Eric Kidd's series+-- of blog posts at <http://www.randomhacks.net/probability-monads/>.+--+-- I have adapted it a bit by making it fit into my own+-- random generation/sampling scheme. +--+-- The idea and purpose of this module should be clear after going+-- through an example. First, let's import the library and 'vector'.+--+-- > import Math.Probable+-- > import qualified Data.Vector as V+--+-- We are going to talk about Books, and particularly about whether a given+-- book is interesting or not.+--+-- > data Book = Interesting +-- >           | Boring+-- >     deriving (Eq, Show)+-- +-- Let's say we have very particular tastes, and that we think+-- that only 20% of all books are interesting (that's not so small actually. oh well).+-- +-- > bookPrior :: Finite d => d Book+-- > bookPrior = weighted [ (Interesting, 0.2) +-- >                      , (Boring, 0.8) +-- >                      ]+-- +-- 'weighted' belongs to the 'Finite' class, which represents +-- types that can somehow represent a distribution over a finite set.+-- That makes our distribution polymorphic in how we will use it. Awesome!+-- +-- So how does it look?+--+-- > λ> exact bookPrior -- in ghci+-- > [Event Interesting 20.0%,Event Boring 80.0%]+--+-- 'exact' takes 'Fin' 'a' and gives you the+-- inner list that 'Fin' uses to represent the distribution.+--+-- Now, what if we pick two books? First, how do we even do that?+-- Well, any instance of 'Finite' must be a 'Monad', so you have your+-- good old /do notation/. The ones provided by this package also+-- provide 'Functor' and 'Applicative' instances, but let's use+-- do.+-- +-- > twoBooks :: Finite d => d (Book, Book)+-- > twoBooks = do+-- >     book1 <- bookPrior+-- >     book2 <- bookPrior+-- >     return (book1, book2)+--+-- Nothing impressive. We pick a book with the prior+-- we defined above, then another, pair them together+-- and hand the pair back. What this will actually do+-- is behave just like in the list monad, but in addition+-- to this it will combine the probabilities of the various+-- events we could be dealing with in the appropriate way.+-- +-- So, how about we verify what I just said:+--+-- > λ> exact twoBooks+-- > [ Event (Interesting,Interesting) 4.0%+-- > , Event (Interesting,Boring) 16.0%+-- > , Event (Boring,Interesting) 16.0%+-- > , Event (Boring,Boring) 64.0%+-- > ]+--+-- Nice! Let's take a look at a more complicated scenario now.+--+-- What if we wanted to take a look at the same distribution,+-- with just a difference: we want at least one of the books to+-- be an Interesting one.+-- +-- > oneInteresting :: Fin (Book, Book)+-- > oneInteresting = bayes $ do -- notice the call to bayes+-- >     (b1, b2) <- twoBooks+-- >     condition (b1 == Interesting || b2 == Interesting)+-- >     return (b1, b2)+-- +-- We get two books from the previous distribution, and use 'condition'+-- to restrict the current distribution to the values of b1 and b2+-- that verify our condition. This lifts us in the 'FinBayes' type,+-- where our probabilistic computations can "fail" in some sense. +-- If you want to discard values and restrict the ones on which you'll+-- run further computations, use 'condition'. +--+-- However, how do we view the distribution now, without having all+-- those 'Maybe's in the middle? That's what 'bayes' is for. It runs+-- the computations for the distribution and discards all the ones+-- where any 'condition' wasn't satisfied. In particular, it means+-- it hands you back a normal 'Fin' distribution.+--+-- If we run this one:+--+-- > λ> exact oneInteresting+-- > [ Event (Interesting,Interesting) 11.1%+-- > , Event (Interesting,Boring) 44.4%+-- > , Event (Boring,Interesting) 44.4%+-- > ]+--+-- Note that these finite distribution types support random sampling too:+--+-- * If one of your distributions has a type like "Finite d => d X",+--   you can actually consider it as a 'RandT' value, from which you can sample.+-- +-- * If you have a 'Fin' distribution, you can use 'liftF' (lift 'Fin')+--   to randomly sample an element from it, by more or less following +--   the distribution's probabilities.+--+-- > -- example of the former+-- > sampleBooks :: RandT IO (V.Vector Book)+-- > sampleBooks = vectorOf 10 bookPrior+-- +-- > λ> mwc sampleBooks+-- > fromList [Interesting,Boring,Boring,Boring,Boring+-- >          ,Boring,Boring,Interesting,Boring,Boring]+--+-- > λ> mwc $ listOf 4 (liftF oneInteresting) -- example of the latter+-- > [ (Boring,Interesting)+-- > , (Boring,Interesting)+-- > , (Boring,Interesting)+-- > , (Interesting,Boring)+-- > ]+module Math.Probable.Distribution.Finite +    ( -- * Probability type +      P(..), prob++      -- * 'Event' type+    , Event(..), never+      -- * 'EventT' monad transformer+    , EventT(..)+      -- * Finite distributions: 'Finite' and 'Fin'+    , Finite(..), Fin, exact, uniformly, liftF+      -- * Bayes' rule: 'FinBayes'+    , FinBayes, bayes, condition, onlyJust+    ) where++import Control.Applicative+import Control.Monad+import Control.Monad.Primitive+import Control.Monad.Trans+import Control.Monad.Trans.Maybe+import Data.List+import Math.Probable.Random++-- | Probability type: wrapper around Double+--   for a nicer Show instance and for more easily+--   enforcing normalization of weights+newtype P = P Double+    deriving (Eq, Ord, Fractional, Num, Real, RealFrac)++-- | Get the underlying probability +--+-- > λ> prob (P 0.1)+-- > 0.1+prob :: P -> Double+prob (P x) = x++instance Show P where+  show (P p) = show intPart ++ "." ++ show fracPart ++ "%"+    where digits = round (1000 * p)+          intPart = digits `div` 10  :: Int+          fracPart = digits `mod` 10 :: Int++-- | An event, and its probability+data Event a = Event a {-# UNPACK #-} !P+    deriving (Eq, Show)++-- | This event never happens (probability of 0)+-- +-- > never = Event undefined 0+never :: Event a+never = Event undefined 0++instance Functor Event where+    fmap f (Event evt p) = Event (f evt) p++instance Applicative Event where+    pure evt = Event evt 1++    Event f p1 <*> Event e p2 +        | p1 == 0 || p2 == 0 = never +        | otherwise          = Event (f e) (p1*p2)++instance Monad Event where+    return evt = Event evt 1++    (Event evt p) >>= f | p == 0 = never+                        | otherwise = Event e' (p*p')+        where Event e' p' = f evt+++-- | 'EventT' monad transformer+-- +-- It pairs a value with a probability within the 'm' monad+newtype EventT m a = EventT { runEventT :: m (Event a) }++instance Monad m => Functor (EventT m) where+    fmap = liftM++instance (Functor m, Monad m) => Applicative (EventT m) where+    pure = return++    mf <*> x = mf >>= \f -> fmap f x++instance Monad m => Monad (EventT m) where+    return = lift . return++    m >>= f = EventT go+        where go = do +                ph <- runEventT m+                case ph of+                    Event e p1 | p1 == 0   -> return never+                               | otherwise -> +                                    do Event e' p2 <- runEventT (f e)+                                       return $ Event e' (p1 * p2)+++instance MonadTrans EventT where+    lift x = EventT (liftM return x)++-- | Create a 'Finite' distribution over the values in+--   the list, each with an equal probability+-- +-- > λ> exact $ uniformly [True, False]+-- > [Event True 50.0%,Event False 50.0%]+uniformly :: Finite d => [a] -> d a+uniformly = weighted . map (,1)+{-# INLINE uniformly #-}++-- | 'Fin' is just 'EventT []'+--+-- You can think of 'Fin a' meaning '[Event a]'+-- i.e a list of the possible outcomes of type 'a'+-- with their respective probability+type Fin = EventT []++-- | See the outcomes of a finite distribution and their probabilities+--+-- > λ> exact $ uniformly [True, False]+-- > [Event True 50.0%,Event False 50.0%]+-- +-- > λ> data Fruit = Apple | Orange deriving (Eq, Show)+-- > λ> exact $ uniformly [Apple, Orange]+-- > [Event Apple 50.0%,Event Orange 50.0%]+-- +-- > λ> exact $ weighted [(Apple, 0.8), (Orange, 0.2)]+-- > [Event Apple 80.0%,Event Orange 20.0%]+exact :: Fin a -> [Event a]+exact = runEventT+{-# INLINE exact #-}++-- | 'FinBayes' is 'Fin' with a 'MaybeT' layer+-- +-- What is that for? The 'MaybeT' lets us express+-- the fact that what we've drawn from the distribution+-- isn't of interest anymore, using 'condition',+-- and observing the remaining cases, using 'bayes',+-- to get back to a normal finite distribution. Example:+--+-- > data Wine = Good | Bad deriving (Eq, Show)+-- > +-- > wines :: Finite d => d Wine+-- > wines = weighted [(Good, 0.2), (Bad, 0.8)]+-- >+-- > twoWines :: Finite d => d (Wine, Wine)+-- > twoWines = (,) <*> wines <$> wines+-- >+-- > decentMeal :: FinBayes (Wine, Wine)+-- > decentMeal = do+-- >   (wine1, wine2) <- twoWines+-- >   -- we only consider the outcomes of 'twoWines' +-- >   -- where at least one of the two wines is good+-- >   -- because we're having a nice meal and are looking+-- >   -- for a decent pair of wine+-- >   condition (wine1 == Good || wine2 == Good)+-- >   return (wine1, wine2)+-- >+-- > -- to view the distribution, applying+-- > -- Bayes' rule on our way:+-- > exact (bayes decentMeal)+type FinBayes = MaybeT Fin++-- | This is the core of 'FinBayes'. If the 'Bool' is false,+--   the current computation is shortcuited (sent to a 'Nothing'+--   in 'MaybeT') and won't be included when running the distribution+--   with 'bayes'. See the documentation of 'FinBayes' for an example.+condition :: Bool -> FinBayes ()+condition = MaybeT . return . toMaybe+    where toMaybe True = Just ()+          toMaybe False = Nothing++-- | This functions discards all the elements of the distribution+--   for which the call to 'condition' yielded 'Nothing'.+--   While 'condition' does the mapping to 'Maybe' values,+--   this function discards all of those values for which the condition+--   was not met.+bayes :: FinBayes a -> Fin a+bayes = onlyJust . runMaybeT++-- | Keeps only the 'Just's and remove the 'Maybe' layer+--   in the distribution.+onlyJust :: Fin (Maybe a) -> Fin a+onlyJust dist+    | total > 0 = EventT (map adjust filtered)+    | otherwise = EventT []+  where filtered = catMaybes' (runEventT dist)+        total = sum (map proba filtered)+        adjust (Event x p) = Event x (p / total)+        proba (Event _ p) = p+        -- value (Event x _) = x++-- | This function, used by 'onlyJust', discards all the events+--   holding a 'Nothing'.+catMaybes' :: [Event (Maybe a)] -> [Event a]+catMaybes' [] = []+catMaybes' (Event Nothing _ : xs) =+  catMaybes' xs+catMaybes' (Event (Just x) p : xs) =+  Event x p : catMaybes' xs++-- | T distribution of probabilities +--   over a finite set.+class (Functor d, Monad d) => Finite d where+    +    -- | The only requirement is to somehow+    --   be able to represent the distribution+    --   corresponding to the list given as argument, e.g:+    --+    -- > weighted [(True, 0.8), (False, 0.2)]+    --+    -- It should also be able to handle the normalization for you.+    --+    -- > weighted [(True, 8), (False, 2)]+    weighted :: [(a, Double)] -> d a++instance Finite Fin where+    weighted l = EventT $ map weight l++        where weight (x, w) = Event x $ P (w / total)+              total         = foldl' (\w (_, w') -> w + w') 0 l++instance PrimMonad m => Finite (RandT m) where+    weighted = liftF . weighted++-- | Make finite distributions ('Fin') citizens of+--   'RandT' by simply sampling an element at random+--   while still approximately preserving the distribution+--+-- > λ> mwc . liftF $ uniformly [True, False]+-- > False+-- > λ> mwc . liftF $ uniformly [True, False]+-- > True+-- > λ> mwc . liftF $ weighted [("Haskell", 99), ("PHP", 1)]+-- > "Haskell"+liftF :: PrimMonad m => Fin a -> RandT m a+liftF dist = do+    d <- double+    pick (P d) (exact dist)++-- | Look for an 'Event' in that list that has+--   a probability superior to the one given as an+--   argument.+pick :: Monad m => P -> [Event a] -> m a+pick _ [] = error "pick: no value to pick"+pick n (Event x p : evts)+    | n <= p    = return x+    | otherwise = pick (n-p) evts++instance Finite FinBayes where+    weighted = lift . weighted
+ src/Math/Probable/Random.hs view
@@ -0,0 +1,483 @@+{-# LANGUAGE BangPatterns,+             TypeFamilies #-}++-- |+-- Module       : Math.Probable+-- Copyright    : (c) 2014 Alp Mestanogullari+-- License      : BSD3+-- Maintainer   : alpmestan@gmail.com+-- Stability    : experimental+-- Portability  : GHC+-- +-- Random number generation based on 'MWC.Gen',+-- defined as a Monad transformer.+-- +-- Quickstart, in ghci:+--+-- > λ> import Math.Probable+-- > λ> import Control.Applicative+-- > λ> mwc double+-- > 0.2756820707828763+-- > λ> mwc word64+-- > 12175918187293541909+-- > λ> mwc $ (,) <$> bool <*> intIn (0, 10)+-- > (True,7)+-- > λ> mwc $ do { n <- intIn (1, 10) ; listOf n (listOf 2 bool) }+-- > [ [False,True],[True,False],[False,True],[False,False],[False,False],+-- >   [False,True],[True,False],[True,False],[True,True],[False,False]+-- > ]+--+-- This module features a bunch of combinators that can help you create+-- some random generation descriptions easily, and in a very familiar style.+--+-- You can easily combine them through the 'Monad' instance for 'RandT'+-- which really just make sure everyone gets a 'MWC.Gen' (from mwc-random)+-- eventually. This of course makes 'RandT' a 'Functor' and an 'Applicative'.+--+-- > import Math.Probable+-- >+-- > data Person = +-- >   Person { name   :: String+-- >          , age    :: Int+-- >          , salary :: Double+-- >          }+-- >     deriving (Eq, Show)+-- > +-- > randomPerson :: PrimMonad m +-- >              => RandT m Person+-- > randomPerson = do+-- >     -- we pick a random length+-- >     -- for the person's name+-- >     nameLen <- intIn (3, 10) +-- >                            +-- >     -- and just express what a random Person+-- >     -- should be, Applicative-style+-- >     Person <$> pickName nameLen    -- pick a name+-- >            <*> intIn (0, 100)      -- an Int between 0 and 100+-- >            <*> doubleIn (0, 10000) -- a Double between 0 and 10000+-- >+-- >     where pickName nameLen = do+-- >               -- the initial, between 'A' and 'Z'+-- >               initial <- chr `fmap` intIn (65, 90)+-- >  +-- >               (initial:) `fmap` +-- >               -- the rest, between 'a' and 'z'+-- >                   listOf (nameLen - 1)+-- >                          (chr `fmap` intIn (97, 122))+-- +-- This is all nice, but how do we actually sample such a Person?+-- You just have to call 'mwc':+-- +-- > λ> mwc randomPerson+-- > Person {name = "Ojeesra", age = 83, salary = 3075.9945184521885}+-- +-- So any value of type 'RandT m a' is something that you'll eventually +-- run in 'm' (hence 'IO' or 'ST' 's') for generating a /random value/ of+-- type 'a'. Note that 'mwc' forces the execution using 'withSystemRandom'+-- and gets you back in 'IO', whereas 'mwcST' gets you back in 'ST' 's'.+-- +-- My simple name generation routine can help you pick a name for your baby,+-- if you are having one soon.+-- +-- > λ> map name `fmap` mwc (listOf 10 randomPerson)+-- > ["Npujbc","Faidx","Zusha","Ghbipic","Ljaestei","Fktcfonnxe","Hlvkolds","Zpws","Zgmrkrdv","Rhcd"]+--+-- If we were to make a generator that could generate more familiar+-- and creativity-free names, we wouldn't sample uniformly+-- from the alphabet.++module Math.Probable.Random +    ( -- * 'RandT' type+      RandT(..)++    , -- * Actually generating random values+      mwc, mwcST+    +    , -- * Combinators for generating individual values+      uniformIn++    , int, int8, int16, int32, int64+    , intIn, int8In, int16In, int32In, int64In++    , word, word8, word16, word32, word64+    , wordIn, word8In, word16In, word32In, word64In++    , float, double+    , floatIn, doubleIn++    , bool+    +    , -- * Filling containers with random values+      listOf, vectorOf, vectorOfVariate+    ) where++import Control.Applicative+import Control.Monad.Identity+import Control.Monad.Primitive+import Control.Monad.ST+import Data.Int+import Data.Word+import qualified Data.Vector.Generic as G+import qualified System.Random.MWC as MWC++-- | 'RandT' type, equivalent to a+--   'ReaderT' ('MWC.Gen' ('PrimState' m))+--   +--   This lets you build simple or complex random generation+--   routines without having the generator passed all around+--   and just run the whole thing in the end, most likely +--   by using 'mwc'.+newtype RandT m a =+    RandT { runRandT :: MWC.Gen (PrimState m) -> m a }++instance Monad m => Monad (RandT m) where+    return x = RandT $ \_ -> return x+    {-# INLINE return #-}++    (RandT g) >>= f = +        RandT $ \gen -> do+            !v <- g gen+            !res <- runRandT (f v) gen+            return res+    {-# INLINE (>>=) #-}++instance Monad m => Functor (RandT m) where+    fmap f r = RandT $ \gen -> return . f =<< runRandT r gen+    {-# INLINE fmap #-}++instance Monad m => Applicative (RandT m) where+    pure = return+    {-# INLINE pure #-}++    (<*>) = ap+    {-# INLINE (<*>) #-}++-- | Generate a random 'Int'. The whole 'Int' range is used.+--+-- > λ> mwc int+-- > 8354496680947360541+int :: PrimMonad m => RandT m Int+int = RandT MWC.uniform+{-# INLINE int #-}++-- | Generate a random 'Int' in the given range.+--+-- > λ> mwc $ intIn (0, 10)+-- > 7+intIn :: PrimMonad m => (Int, Int) -> RandT m Int+intIn (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE intIn #-}++-- | Generate a random 'Int8'. The whole 'Int8' range is used.+--+-- > λ> mwc int8+-- > -65+int8 :: PrimMonad m => RandT m Int8+int8 = RandT MWC.uniform+{-# INLINE int8 #-}++-- | Generate a random 'Int8' in the given range+--+-- > λ> mwc $ int8In (-10, 10)+-- > -3+int8In :: PrimMonad m => (Int8, Int8) -> RandT m Int8+int8In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE int8In #-}++-- | Generate a random 'Int16'. The whole 'Int16' range is used.+--+-- > λ> mwc int16+-- > 15413+int16 :: PrimMonad m => RandT m Int16+int16 = RandT MWC.uniform+{-# INLINE int16 #-}++-- | Generate a random 'Int16' in the given range+--+-- > λ> mwc $ int16In (-500, 30129)+-- > 9501+int16In :: PrimMonad m => (Int16, Int16) -> RandT m Int16+int16In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE int16In #-}++-- | Generate a random 'Int32'. The whole 'Int32' range is used.+--+-- > λ> mwc int32+-- > 1774441747+int32 :: PrimMonad m => RandT m Int32+int32 = RandT MWC.uniform+{-# INLINE int32 #-}++-- | Generate a random 'Int32' in the given range.+--+-- > λ> mwc $ int32In (-500, 30129)+-- > 8012+int32In :: PrimMonad m => (Int32, Int32) -> RandT m Int32+int32In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE int32In #-}++-- | Generate a random 'Int64'. The whole 'Int64' range is used.+--+-- > λ> mwc int64+-- > -2596387699802756017+int64 :: PrimMonad m => RandT m Int64+int64 = RandT MWC.uniform+{-# INLINE int64 #-}++-- | Generate a random 'Int64' in the given range.+--+-- > λ> mwc $ int64In (-2^30, 30)+-- > -630614786+int64In :: PrimMonad m => (Int64, Int64) -> RandT m Int64+int64In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE int64In #-}++-- | Generate a random 'Word'. The whole 'Word' range is used.+-- +-- > λ> mwc word+-- > 3106215968599504888+word :: PrimMonad m => RandT m Word+word = RandT MWC.uniform+{-# INLINE word #-}++-- | Generate a random 'Word' in the given range.+--+-- > λ> mwc $ wordIn (1, 64)+-- > 28+wordIn :: PrimMonad m => (Word, Word) -> RandT m Word+wordIn (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE wordIn #-}++-- | Generate a random 'Word8'. The whole 'Word8' range is used.+-- +-- > λ> mwc word8+-- > 231+word8 :: PrimMonad m => RandT m Word8+word8 = RandT MWC.uniform+{-# INLINE word8 #-}++-- | Generate a random 'Word8' in the given range+-- +-- > λ> mwc $ word8In (2, 15)+-- > 3+word8In :: PrimMonad m => (Word8, Word8) -> RandT m Word8+word8In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE word8In #-}++-- | Generate a random 'Word16'. The whole 'Word16' range is used.+--+-- > λ> mwc word16+-- > 31127+word16 :: PrimMonad m => RandT m Word16+word16 = RandT MWC.uniform+{-# INLINE word16 #-}++-- | Generate a random 'Word16' in the given range.+--+-- > λ> mwc $ word16In (2^13, 2^14)+-- > 8885+word16In :: PrimMonad m => (Word16, Word16) -> RandT m Word16+word16In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE word16In #-}++-- | Generate a random 'Word32'. The whole 'Word32' range is used.+--+-- > λ> mwc word32+-- > 3917666696+word32 :: PrimMonad m => RandT m Word32+word32 = RandT MWC.uniform+{-# INLINE word32 #-}++-- | Generate a random 'Word32' in the given range.+--+-- > λ> mwc $ word32In (100, 330)+-- > 125+word32In :: PrimMonad m => (Word32, Word32) -> RandT m Word32+word32In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE word32In #-}++-- | Generate a random 'Word64'. The whole 'Word64' range is used.+--+-- > λ> mwc word64+-- > 12496697905424132339+word64 :: PrimMonad m => RandT m Word64+word64 = RandT MWC.uniform+{-# INLINE word64 #-}++-- | Generate a random 'Word64' in the given range.+--+-- > λ> mwc $ word64In (2^45, 2^46)+-- > 59226619151303+word64In :: PrimMonad m => (Word64, Word64) -> RandT m Word64+word64In (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE word64In #-}++-- | Generate a random 'Float' between 0 (excluded)+--   and 1 (included)+-- +-- > λ> mwc float+-- > 0.11831179+float :: PrimMonad m => RandT m Float+float = RandT MWC.uniform+{-# INLINE float #-}++-- | Generate a random 'Float' in the given range+--+-- > λ> mwc $ floatIn (0.20, 3.14)+-- > 1.3784513+floatIn :: PrimMonad m => (Float, Float) -> RandT m Float+floatIn (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE floatIn #-}++-- | Generate a random 'Double' between 0 (excluded)+--   and 1 (included)+-- +-- > λ> mwc double+-- > 0.7689412928620208+double :: PrimMonad m => RandT m Double+double = RandT MWC.uniform+{-# INLINE double #-}++-- | Generate a random 'Double' in the given range+-- +-- > λ> mwc $ doubleIn (-30.121121445, 0.129898878612)+-- > -13.612464813256999+doubleIn :: PrimMonad m => (Double, Double) -> RandT m Double+doubleIn (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE doubleIn #-}++-- | Generate a random 'Bool'+--+-- > λ> mwc bool+-- > False+bool :: PrimMonad m => RandT m Bool+bool = RandT MWC.uniform+{-# INLINE bool #-}++-- | A generic function for sampling uniformly any type+--   that implements 'MWC.Variate'.+--+--   All the 'xxxIn' functions from this module just+--   call 'MWC.uniformR'.+uniformIn :: (MWC.Variate a, PrimMonad m) => (a, a) -> RandT m a+uniformIn (a, b) = RandT $ MWC.uniformR (a, b)+{-# INLINE uniformIn #-}++-- | Take a 'RandT' value and run it in 'IO',+--   generating all the random values described by+--   the 'RandT'. It just uses 'MWC.withSystemRandom'+--   so you really should try hard to put your whole+--   random generation logic in 'RandT' and call +--   'mwc' in the end, thus initialising the generator+--   only once and generating everything with it.+--+--   See the documentation for 'MWC.withSystemRandom' for more about this. +-- +-- > λ> mwc $ (+2) `fmap` int8+-- > 34+mwc :: RandT IO a+    -> IO a+mwc = MWC.withSystemRandom +    . MWC.asGenIO +    . runRandT+{-# INLINE mwc #-}++-- | If for some reason you have a 'RandT' ('ST' 's')+--   you can run it from 'IO' just like we do with 'mwc'.+-- +-- > λ> mwcST $ listOf 4 bool+-- > [False,False,True,True]+mwcST :: RandT (ST s) a+      -> IO a+mwcST = MWC.withSystemRandom+      . MWC.asGenST+      . runRandT+{-# INLINE mwcST #-}++-- | Repeatedly run a random computation+--   yielding a value of type 'a' to get +--   a list of random values of type 'a'.+-- +-- > λ> mwc (listOf 30 float)+-- > [ 5.438623e-2,0.78114086,0.4954672,0.5958733,0.47243807,5.883485e-2+-- > , 5.500287e-2,0.79262286,0.5528683,0.7628807,0.80705905,0.15368962+-- > , 0.8654971,0.4560417,0.23922172,0.5069659,0.8130155,0.6559351+-- > , 1.31405e-2,0.25705606,0.7134138,0.79111993,0.7529769,0.10573909+-- > , 0.37731406,0.6289338,0.85156864,0.15691182,0.9910314,8.133593e-2+-- > ]+--+-- > λ> mwc (sum `fmap` listOf 30 float)+-- > 15.037931+listOf :: Monad m +       => Int+       -> RandT m a+       -> RandT m [a] +listOf n r = replicateM n r+{-# INLINE listOf #-}++-- | A function for generating a vector+--   of the given length for values+--   whose types are instances of 'MWC.Variate'.+-- +--   This function is generic in the type of vector it returns,+--   any instance of 'G.Vector' will do.+-- +--   It's just a wrapper arround 'MWC.uniformVector'+--   and doesn't really use the 'Monad' instance of 'RandT'.+--+--   But if you want to have a vector of 'Person's, +--   you have to use 'vectorOf'.+-- +-- > λ> import qualified Data.Vector.Unboxed as V+-- > λ> :set -XScopedTypeVariables+-- > λ> v :: V.Vector Double <- mwc $ vectorOfVariate 10+-- > λ> V.mapM_ print v+-- > 3.8565084196117705e-2+-- > 0.575103826646098+-- > 0.379710162825715+-- > 0.4066991135077237+-- > 0.9778431248247549+-- > 0.3786223745680838+-- > 0.4361789615081698+-- > 0.9904407826187301+-- > 0.2951087330670904+-- > 0.1533350329892028+vectorOfVariate :: (PrimMonad m, MWC.Variate a, G.Vector v a)+                => Int+                -> RandT m (v a)+vectorOfVariate n = +    RandT $ \gen -> MWC.uniformVector gen n+{-# INLINE vectorOfVariate #-}++-- | A function for generating a vector of the given+--   length with random values /of any type/ +--   (in contrast to 'vectorOfVariate').+--+--   It is generic in the 'G.Vector' instance it+--   hands you back. It's implemented in terms of+--   'G.replicateM' and has been benchmarked to perform+--   as well as 'MWC.uniformVector' on simple types+--   ('MWC.uniformVector' can't generate values for types+--   that don't have a 'MWC.Variate' instance).+--+-- > λ> import qualified Data.Vector.Unboxed as V+-- > λ> :set -XScopedTypeVariables+-- > λ> v :: V.Vector Int <- mwc $ vectorOf 10 int+-- > λ> V.mapM_ print v+-- > -3920053790769159788+-- > 3983393642052845448+-- > 1528310798822685910+-- > 3522283620461337684+-- > 6451017362937898910+-- > 1929485210691770214+-- > 8547527164583329795+-- > 3298785082692387491+-- > 4019024417224980311+-- > -5216301990322376953+vectorOf :: (Monad m, G.Vector v a)+         => Int+         -> RandT m a+         -> RandT m (v a)+vectorOf n r =+    RandT $ \gen -> G.replicateM n (runRandT r gen)+{-# INLINE vectorOf #-}