module Simulation.Aivika.Generator
(Generator(..),
GeneratorType(..),
newGenerator,
newRandomGenerator) where
import System.Random
import Data.IORef
data Generator =
Generator { generatorUniform :: Double -> Double -> IO Double,
generatorUniformInt :: Int -> Int -> IO Int,
generatorNormal :: Double -> Double -> IO Double,
generatorExponential :: Double -> IO Double,
generatorErlang :: Double -> Int -> IO Double,
generatorPoisson :: Double -> IO Int,
generatorBinomial :: Double -> Int -> IO Int
}
generateUniform :: IO Double
-> Double
-> Double
-> IO Double
generateUniform g min max =
do x <- g
return $ min + x * (max min)
generateUniformInt :: IO Double
-> Int
-> Int
-> IO Int
generateUniformInt g min max =
do x <- g
let min' = fromIntegral min
max' = fromIntegral max
return $ round (min' + x * (max' min'))
newNormalGenerator :: IO Double
-> IO (IO Double)
newNormalGenerator 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
generateExponential :: IO Double
-> Double
-> IO Double
generateExponential g mu =
do x <- g
return ( log x * mu)
generateErlang :: IO Double
-> Double
-> Int
-> IO Double
generateErlang 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)
generatePoisson :: IO Double
-> Double
-> IO Int
generatePoisson 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
generateBinomial :: IO Double
-> Double
-> Int
-> IO Int
generateBinomial 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
data GeneratorType = SimpleGenerator
| SimpleGeneratorWithSeed Int
| CustomGenerator (IO Generator)
| CustomGenerator01 (IO Double)
newGenerator :: GeneratorType -> IO Generator
newGenerator tp =
case tp of
SimpleGenerator ->
newStdGen >>= newRandomGenerator
SimpleGeneratorWithSeed x ->
newRandomGenerator $ mkStdGen x
CustomGenerator g ->
g
CustomGenerator01 g ->
newRandomGenerator01 g
newRandomGenerator :: RandomGen g => g -> IO Generator
newRandomGenerator g =
do r <- newIORef g
let g1 = do g <- readIORef r
let (x, g') = random g
writeIORef r g'
return x
newRandomGenerator01 g1
newRandomGenerator01 :: IO Double -> IO Generator
newRandomGenerator01 g =
do let g1 = g
g2 <- newNormalGenerator g1
let g3 mu nu =
do x <- g2
return $ mu + nu * x
return Generator { generatorUniform = generateUniform g1,
generatorUniformInt = generateUniformInt g1,
generatorNormal = g3,
generatorExponential = generateExponential g1,
generatorErlang = generateErlang g1,
generatorPoisson = generatePoisson g1,
generatorBinomial = generateBinomial g1 }