{-# LANGUAGE FlexibleContexts #-} -- | -- Module : Simulation.Aivika.Statistics -- Copyright : Copyright (c) 2009-2012, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 7.0.3 -- -- Represents statistics and results. -- module Simulation.Aivika.Statistics (Statistics, newStatistics, addStatistics, statisticsData, analyzeData, AnalysisResults(..), showResults) where import Data.Foldable import Data.Array import Data.Array.IO import Control.Monad import Control.Monad.Trans import Control.Concurrent.MVar import Simulation.Aivika.UVector -- | Represents statistics. -- -- All functions with the statistics in this module are thread-safe. Therefore -- you can use them in experiments when parallel simulations execute simultaneously. data Statistics a = Statistics { statData :: UVector a, statLock :: MVar () } -- | Create new statistics. newStatistics :: (MArray IOUArray a IO) => IO (Statistics a) newStatistics = do v <- newVector l <- newMVar () return Statistics { statData = v, statLock = l } -- | Add data to the statistics. It is thread-safe. addStatistics :: (MArray IOUArray a IO) => Statistics a -> a -> IO () addStatistics s x = withMVar (statLock s) $ \() -> appendVector (statData s) x -- | Return the statistics data. It is thread-safe. statisticsData :: (MArray IOUArray a IO) => Statistics a -> IO (Array Int a) statisticsData s = withMVar (statLock s) $ \() -> freezeVector (statData s) -- | Represents the results of the statistic analysis. data AnalysisResults a = AnalysisResults { resultsData :: Array Int a, -- ^ Statistic data. resultsMean :: Double, -- ^ The average value. resultsVariance :: Double, -- ^ The variance. resultsMin :: a, -- ^ The minimum value. resultsMax :: a -- ^ The maximum value. } deriving (Eq, Ord, Show) -- | Analyze data. analyzeData :: Real a => Array Int a -> AnalysisResults a analyzeData xs = let (i1, i2) = bounds xs meanx = foldl' (\y i -> y * (1 - k i) + f i * k i) 0 [i1 .. i2] sqrx = foldl' (\y i -> y * (1 - k i) + g i * k i) 0 [i1 .. i2] minx = foldl' (\y i -> if i == 0 then x i else min y (x i)) 0 [i1 .. i2] maxx = foldl' (\y i -> if i == 0 then x i else max y (x i)) 0 [i1 .. i2] x i = xs ! i f i = fromRational (toRational (x i)) g i = let y = f i in y * y k i = 1 / fromInteger (toInteger (i - i1 + 1)) in AnalysisResults { resultsData = xs, resultsMean = meanx, resultsVariance = sqrx - meanx * meanx, resultsMin = minx, resultsMax = maxx } -- | Show the results of analysis with the specified indent. showResults :: (Show a) => AnalysisResults a -> Int -> ShowS showResults rs indent = let (i1, i2) = bounds (resultsData rs) tab = replicate indent ' ' in if i1 <= i2 then showString tab . showString "mean = " . shows (resultsMean rs) . showString "\n" . showString tab . showString "deviation = " . shows (sqrt (resultsVariance rs)) . showString "\n" . showString tab . showString "minimum = " . shows (resultsMin rs) . showString "\n" . showString tab . showString "maximum = " . shows (resultsMax rs) else showString tab . showString "mean = ---" . showString "\n" . showString tab . showString "deviation = ---" . showString "\n" . showString tab . showString "minimum = ---" . showString "\n" . showString tab . showString "maximum = ---"