-- | -- Module : Simulation.Aivika.Dynamics.Parameter -- Copyright : Copyright (c) 2009-2013, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 7.6.3 -- -- This module defines the parameters of simulation experiments. -- module Simulation.Aivika.Dynamics.Parameter (newParameter, newTableParameter, newIndexedParameter, newRandomParameter, newNormalParameter) where import Data.Array import Data.IORef import qualified Data.Map as M import Control.Concurrent.MVar import System.Random import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.Random -- | Create a thread-safe parameter that returns always the same value within the simulation run, -- where the value is recalculated for each new run. newParameter :: IO a -> IO (Simulation a) newParameter a = newIndexedParameter $ \_ -> a -- | Create a thread-safe parameter that returns always the same value within the simulation run, -- where the value is taken consequently from the specified table based on the number of the -- current run starting from zero. After all values from the table are used, it takes the first -- value of the table, then the second one and so on. newTableParameter :: Array Int a -> IO (Simulation a) newTableParameter t = newIndexedParameter (\i -> return $ t ! (((i - i1) `mod` n) + i1)) where (i1, i2) = bounds t n = i2 - i1 + 1 -- | Create a thread-safe parameter that returns always the same value within the simulation run, -- where the value depends on the number of this run starting from zero. newIndexedParameter :: (Int -> IO a) -> IO (Simulation a) newIndexedParameter f = do lock <- newMVar () dict <- newIORef M.empty return $ Simulation $ \r -> do let i = runIndex r m <- readIORef dict if M.member i m then do let Just v = M.lookup i m return v else withMVar lock $ \() -> do { m <- readIORef dict; if M.member i m then do let Just v = M.lookup i m return v else do v <- f i writeIORef dict $ M.insert i v m return v } -- | Create a new random parameter distributed uniformly. -- The value doesn't change within the simulation run but -- then the value is recalculated for each new run. newRandomParameter :: Simulation Double -- ^ minimum -> Simulation Double -- ^ maximum -> IO (Simulation Double) newRandomParameter min max = do x <- newParameter $ getStdRandom random return $ min + x * (max - min) -- | Create a new random parameter distributed normally. -- The value doesn't change within the simulation run but -- then the value is recalculated for each new run. newNormalParameter :: Simulation Double -- ^ mean -> Simulation Double -- ^ variance -> IO (Simulation Double) newNormalParameter mu nu = do x <- normalGen >>= newParameter return $ mu + x * nu