module Simulation.Aivika.Trans.Generator
(GeneratorMonad(..),
GeneratorType(..)) where
import System.Random
import Data.IORef
import Simulation.Aivika.Trans.Session
class (Functor m, Monad m) => GeneratorMonad m where
data Generator m :: *
generateUniform :: Generator m -> Double -> Double -> m Double
generateUniformInt :: Generator m -> Int -> Int -> m Int
generateNormal :: Generator m -> Double -> Double -> m Double
generateExponential :: Generator m -> Double -> m Double
generateErlang :: Generator m -> Double -> Int -> m Double
generatePoisson :: Generator m -> Double -> m Int
generateBinomial :: Generator m -> Double -> Int -> m Int
newGenerator :: Session m -> GeneratorType m -> m (Generator m)
newRandomGenerator :: RandomGen g => Session m -> g -> m (Generator m)
newRandomGenerator01 :: Session m -> m Double -> m (Generator m)
instance GeneratorMonad IO where
data Generator IO =
Generator { generator01 :: IO Double,
generatorNormal01 :: IO Double
}
generateUniform = generateUniform01 . generator01
generateUniformInt = generateUniformInt01 . generator01
generateNormal = generateNormal01 . generatorNormal01
generateExponential = generateExponential01 . generator01
generateErlang = generateErlang01 . generator01
generatePoisson = generatePoisson01 . generator01
generateBinomial = generateBinomial01 . generator01
newGenerator session tp =
case tp of
SimpleGenerator ->
newStdGen >>= newRandomGenerator session
SimpleGeneratorWithSeed x ->
newRandomGenerator session $ mkStdGen x
CustomGenerator g ->
g
CustomGenerator01 g ->
newRandomGenerator01 session g
newRandomGenerator session g =
do r <- newIORef g
let g01 = do g <- readIORef r
let (x, g') = random g
writeIORef r g'
return x
newRandomGenerator01 session g01
newRandomGenerator01 session g01 =
do gNormal01 <- newNormalGenerator01 g01
return Generator { generator01 = g01,
generatorNormal01 = gNormal01 }
data GeneratorType m = SimpleGenerator
| SimpleGeneratorWithSeed Int
| CustomGenerator (m (Generator m))
| CustomGenerator01 (m Double)
generateUniform01 :: IO Double
-> Double
-> Double
-> IO Double
generateUniform01 g min max =
do x <- g
return $ min + x * (max min)
generateUniformInt01 :: IO Double
-> Int
-> Int
-> IO Int
generateUniformInt01 g min max =
do x <- g
let min' = fromIntegral min
max' = fromIntegral max
return $ round (min' + x * (max' min'))
generateNormal01 :: IO Double
-> Double
-> Double
-> IO Double
generateNormal01 g mu nu =
do x <- g
return $ mu + nu * x
newNormalGenerator01 :: IO Double
-> IO (IO Double)
newNormalGenerator01 g =
do nextRef <- newIORef 0.0
flagRef <- newIORef False
xi1Ref <- newIORef 0.0
xi2Ref <- newIORef 0.0
psiRef <- newIORef 0.0
let loop =
do psi <- readIORef psiRef
if (psi >= 1.0) || (psi == 0.0)
then do g1 <- g
g2 <- g
let xi1 = 2.0 * g1 1.0
xi2 = 2.0 * g2 1.0
psi = xi1 * xi1 + xi2 * xi2
writeIORef xi1Ref xi1
writeIORef xi2Ref xi2
writeIORef psiRef psi
loop
else writeIORef psiRef $ sqrt ( 2.0 * log psi / psi)
return $
do flag <- readIORef flagRef
if flag
then do writeIORef flagRef False
readIORef nextRef
else do writeIORef xi1Ref 0.0
writeIORef xi2Ref 0.0
writeIORef psiRef 0.0
loop
xi1 <- readIORef xi1Ref
xi2 <- readIORef xi2Ref
psi <- readIORef psiRef
writeIORef flagRef True
writeIORef nextRef $ xi2 * psi
return $ xi1 * psi
generateExponential01 :: IO Double
-> Double
-> IO Double
generateExponential01 g mu =
do x <- g
return ( log x * mu)
generateErlang01 :: IO Double
-> Double
-> Int
-> IO Double
generateErlang01 g beta m =
do x <- loop m 1
return ( log x * beta)
where loop m acc
| m < 0 = error "Negative shape: generateErlang."
| m == 0 = return acc
| otherwise = do x <- g
loop (m 1) (x * acc)
generatePoisson01 :: IO Double
-> Double
-> IO Int
generatePoisson01 g mu =
do prob0 <- g
let loop prob prod acc
| prob <= prod = return acc
| otherwise = loop
(prob prod)
(prod * mu / fromIntegral (acc + 1))
(acc + 1)
loop prob0 (exp ( mu)) 0
generateBinomial01 :: IO Double
-> Double
-> Int
-> IO Int
generateBinomial01 g prob trials = loop trials 0 where
loop n acc
| n < 0 = error "Negative number of trials: generateBinomial."
| n == 0 = return acc
| otherwise = do x <- g
if x <= prob
then loop (n 1) (acc + 1)
else loop (n 1) acc