module Simulation.Aivika.Statistics
(
SamplingStats(..),
SamplingData(..),
combineSamplingStatsEither,
samplingStatsVariance,
samplingStatsDeviation,
samplingStatsSummary,
returnSamplingStats,
listSamplingStats,
fromIntSamplingStats,
TimingStats(..),
TimingData(..),
timingStatsDeviation,
timingStatsSummary,
returnTimingStats,
fromIntTimingStats) where
import Data.Monoid
class Ord a => ConvertableToDouble a where
convertToDouble :: a -> Double
instance ConvertableToDouble Double where
convertToDouble = id
instance ConvertableToDouble Int where
convertToDouble = fromIntegral
data SamplingStats a =
SamplingStats { samplingStatsCount :: !Int,
samplingStatsMin :: !a,
samplingStatsMax :: !a,
samplingStatsMean :: !Double,
samplingStatsMean2 :: !Double
}
deriving (Eq, Ord)
class SamplingData a where
emptySamplingStats :: SamplingStats a
addSamplingStats :: a -> SamplingStats a -> SamplingStats a
combineSamplingStats :: SamplingStats a -> SamplingStats a -> SamplingStats a
instance SamplingData a => Monoid (SamplingStats a) where
mempty = emptySamplingStats
mappend = combineSamplingStats
instance SamplingData Double where
emptySamplingStats =
SamplingStats { samplingStatsCount = 0,
samplingStatsMin = 1 / 0,
samplingStatsMax = (1) / 0,
samplingStatsMean = 0 / 0,
samplingStatsMean2 = 0 / 0 }
addSamplingStats = addSamplingStatsGeneric
combineSamplingStats = combineSamplingStatsGeneric
instance SamplingData Int where
emptySamplingStats =
SamplingStats { samplingStatsCount = 0,
samplingStatsMin = maxBound,
samplingStatsMax = minBound,
samplingStatsMean = 0 / 0,
samplingStatsMean2 = 0 / 0 }
addSamplingStats = addSamplingStatsGeneric
combineSamplingStats = combineSamplingStatsGeneric
addSamplingStatsGeneric :: ConvertableToDouble a => a -> SamplingStats a -> SamplingStats a
addSamplingStatsGeneric a stats
| isNaN x = stats
| count == 1 = SamplingStats { samplingStatsCount = 1,
samplingStatsMin = a,
samplingStatsMax = a,
samplingStatsMean = x,
samplingStatsMean2 = x * x }
| otherwise = SamplingStats { samplingStatsCount = count,
samplingStatsMin = minX,
samplingStatsMax = maxX,
samplingStatsMean = meanX,
samplingStatsMean2 = meanX2 }
where count = 1 + samplingStatsCount stats
minX = a `seq` min a (samplingStatsMin stats)
maxX = a `seq` max a (samplingStatsMax stats)
meanX = k1 * x + k2 * samplingStatsMean stats
meanX2 = k1 * x * x + k2 * samplingStatsMean2 stats
n = fromIntegral count
x = convertToDouble a
k1 = 1.0 / n
k2 = (n 1.0) / n
combineSamplingStatsGeneric :: ConvertableToDouble a =>
SamplingStats a -> SamplingStats a -> SamplingStats a
combineSamplingStatsGeneric stats1 stats2
| c1 == 0 = stats2
| c2 == 0 = stats1
| otherwise = SamplingStats { samplingStatsCount = c,
samplingStatsMin = minZ,
samplingStatsMax = maxZ,
samplingStatsMean = meanZ,
samplingStatsMean2 = meanZ2 }
where c1 = samplingStatsCount stats1
c2 = samplingStatsCount stats2
c = c1 + c2
n1 = fromIntegral c1
n2 = fromIntegral c2
n = n1 + n2
minX = samplingStatsMin stats1
minY = samplingStatsMin stats2
minZ = min minX minY
maxX = samplingStatsMax stats1
maxY = samplingStatsMax stats2
maxZ = max maxX maxY
meanX = samplingStatsMean stats1
meanY = samplingStatsMean stats2
meanZ = k1 * meanX + k2 * meanY
meanX2 = samplingStatsMean2 stats1
meanY2 = samplingStatsMean2 stats2
meanZ2 = k1 * meanX2 + k2 * meanY2
k1 = n1 / n
k2 = n2 / n
combineSamplingStatsEither :: SamplingData a => Either a (SamplingStats a) -> SamplingStats a -> SamplingStats a
combineSamplingStatsEither (Left a) stats2 = addSamplingStats a stats2
combineSamplingStatsEither (Right stats1) stats2 = combineSamplingStats stats1 stats2
samplingStatsVariance :: SamplingStats a -> Double
samplingStatsVariance stats
| count == 1 = 0
| otherwise = (meanX2 meanX * meanX) * (n / (n 1))
where count = samplingStatsCount stats
meanX = samplingStatsMean stats
meanX2 = samplingStatsMean2 stats
n = fromIntegral count
samplingStatsDeviation :: SamplingStats a -> Double
samplingStatsDeviation = sqrt . samplingStatsVariance
returnSamplingStats :: SamplingData a => a -> SamplingStats a
returnSamplingStats x = addSamplingStats x emptySamplingStats
listSamplingStats :: SamplingData a => [a] -> SamplingStats a
listSamplingStats = foldr addSamplingStats emptySamplingStats
fromIntSamplingStats :: SamplingStats Int -> SamplingStats Double
fromIntSamplingStats stats =
stats { samplingStatsMin = fromIntegral $ samplingStatsMin stats,
samplingStatsMax = fromIntegral $ samplingStatsMax stats }
showSamplingStats :: (Show a) => SamplingStats a -> ShowS
showSamplingStats stats =
showString "{ count = " . shows (samplingStatsCount stats) .
showString ", mean = " . shows (samplingStatsMean stats) .
showString ", std = " . shows (samplingStatsDeviation stats) .
showString ", min = " . shows (samplingStatsMin stats) .
showString ", max = " . shows (samplingStatsMax stats) .
showString " }"
instance Show a => Show (SamplingStats a) where
showsPrec prec = showSamplingStats
samplingStatsSummary :: (Show a) => SamplingStats a -> Int -> ShowS
samplingStatsSummary stats indent =
let tab = replicate indent ' '
in showString tab .
showString "count = " . shows (samplingStatsCount stats) .
showString "\n" .
showString tab .
showString "mean = " . shows (samplingStatsMean stats) .
showString "\n" .
showString tab .
showString "std = " . shows (samplingStatsDeviation stats) .
showString "\n" .
showString tab .
showString "min = " . shows (samplingStatsMin stats) .
showString "\n" .
showString tab .
showString "max = " . shows (samplingStatsMax stats)
data TimingStats a =
TimingStats { timingStatsCount :: !Int,
timingStatsMin :: !a,
timingStatsMax :: !a,
timingStatsMinTime :: !Double,
timingStatsMaxTime :: !Double,
timingStatsStartTime :: !Double,
timingStatsLastTime :: !Double,
timingStatsSum :: !Double,
timingStatsSum2 :: !Double
} deriving (Eq, Ord)
class TimingData a where
emptyTimingStats :: TimingStats a
addTimingStats :: Double -> a -> TimingStats a -> TimingStats a
timingStatsMean :: TimingStats a -> Double
timingStatsVariance :: TimingStats a -> Double
instance TimingData Double where
emptyTimingStats =
TimingStats { timingStatsCount = 0,
timingStatsMin = 1 / 0,
timingStatsMax = (1) / 0,
timingStatsMinTime = 1 / 0,
timingStatsMaxTime = (1) / 0,
timingStatsStartTime = 1 / 0,
timingStatsLastTime = (1) / 0,
timingStatsSum = 0 / 0,
timingStatsSum2 = 0 / 0 }
addTimingStats = addTimingStatsGeneric
timingStatsMean = timingStatsMeanGeneric
timingStatsVariance = timingStatsVarianceGeneric
instance TimingData Int where
emptyTimingStats =
TimingStats { timingStatsCount = 0,
timingStatsMin = maxBound,
timingStatsMax = minBound,
timingStatsMinTime = 1 / 0,
timingStatsMaxTime = (1) / 0,
timingStatsStartTime = 1 / 0,
timingStatsLastTime = (1) / 0,
timingStatsSum = 0 / 0,
timingStatsSum2 = 0 / 0 }
addTimingStats = addTimingStatsGeneric
timingStatsMean = timingStatsMeanGeneric
timingStatsVariance = timingStatsVarianceGeneric
addTimingStatsGeneric :: ConvertableToDouble a => Double -> a -> TimingStats a -> TimingStats a
addTimingStatsGeneric t a stats
| t < t' = error "The current time cannot be less than the previous one: addTimingStats"
| isNaN x = stats
| count == 1 = TimingStats { timingStatsCount = 1,
timingStatsMin = a,
timingStatsMax = a,
timingStatsMinTime = t,
timingStatsMaxTime = t,
timingStatsStartTime = t,
timingStatsLastTime = t,
timingStatsSum = 0,
timingStatsSum2 = 0 }
| otherwise = TimingStats { timingStatsCount = count,
timingStatsMin = minX,
timingStatsMax = maxX,
timingStatsMinTime = minT,
timingStatsMaxTime = maxT,
timingStatsStartTime = t0,
timingStatsLastTime = t,
timingStatsSum = sumX,
timingStatsSum2 = sumX2 }
where count = 1 + timingStatsCount stats
minX' = timingStatsMin stats
maxX' = timingStatsMax stats
minX = a `seq` min a minX'
maxX = a `seq` max a maxX'
minT | a < minX' = t
| otherwise = timingStatsMinTime stats
maxT | a > maxX' = t
| otherwise = timingStatsMaxTime stats
t0 = timingStatsStartTime stats
t' = timingStatsLastTime stats
x = convertToDouble a
sumX' = timingStatsSum stats
sumX = sumX' + (t t') * x
sumX2' = timingStatsSum2 stats
sumX2 = sumX2' + (t t') * x * x
timingStatsMeanGeneric :: ConvertableToDouble a => TimingStats a -> Double
timingStatsMeanGeneric stats
| count == 0 = 0 / 0
| t1 > t0 = sumX / (t1 t0)
| otherwise = minX
where t0 = timingStatsStartTime stats
t1 = timingStatsLastTime stats
sumX = timingStatsSum stats
minX = convertToDouble $ timingStatsMin stats
count = timingStatsCount stats
timingStatsMean2Generic :: ConvertableToDouble a => TimingStats a -> Double
timingStatsMean2Generic stats
| count == 0 = 0 / 0
| t1 > t0 = sumX2 / (t1 t0)
| otherwise = minX * minX
where t0 = timingStatsStartTime stats
t1 = timingStatsLastTime stats
sumX2 = timingStatsSum2 stats
minX = convertToDouble $ timingStatsMin stats
count = timingStatsCount stats
timingStatsVarianceGeneric :: ConvertableToDouble a => TimingStats a -> Double
timingStatsVarianceGeneric stats = ex2 ex * ex
where ex = timingStatsMeanGeneric stats
ex2 = timingStatsMean2Generic stats
timingStatsDeviation :: TimingData a => TimingStats a -> Double
timingStatsDeviation = sqrt . timingStatsVariance
returnTimingStats :: TimingData a => Double -> a -> TimingStats a
returnTimingStats t a = addTimingStats t a emptyTimingStats
fromIntTimingStats :: TimingStats Int -> TimingStats Double
fromIntTimingStats stats =
stats { timingStatsMin = fromIntegral $ timingStatsMin stats,
timingStatsMax = fromIntegral $ timingStatsMax stats }
showTimingStats :: (Show a, TimingData a) => TimingStats a -> ShowS
showTimingStats stats =
showString "{ count = " . shows (timingStatsCount stats) .
showString ", mean = " . shows (timingStatsMean stats) .
showString ", std = " . shows (timingStatsDeviation stats) .
showString ", min = " . shows (timingStatsMin stats) .
showString " (t = " . shows (timingStatsMinTime stats) .
showString "), max = " . shows (timingStatsMax stats) .
showString " (t = " . shows (timingStatsMaxTime stats) .
showString "), t in [" . shows (timingStatsStartTime stats) .
showString ", " . shows (timingStatsLastTime stats) .
showString "] }"
instance (Show a, TimingData a) => Show (TimingStats a) where
showsPrec prec = showTimingStats
timingStatsSummary :: (Show a, TimingData a) => TimingStats a -> Int -> ShowS
timingStatsSummary stats indent =
let tab = replicate indent ' '
in showString tab .
showString "count = " . shows (timingStatsCount stats) .
showString "\n" .
showString tab .
showString "mean = " . shows (timingStatsMean stats) .
showString "\n" .
showString tab .
showString "std = " . shows (timingStatsDeviation stats) .
showString "\n" .
showString tab .
showString "min = " . shows (timingStatsMin stats) .
showString " (t = " . shows (timingStatsMinTime stats) .
showString ")\n" .
showString tab .
showString "max = " . shows (timingStatsMax stats) .
showString " (t = " . shows (timingStatsMaxTime stats) .
showString ")\n" .
showString tab .
showString "t in [" . shows (timingStatsStartTime stats) .
showString ", " . shows (timingStatsLastTime stats) .
showString "]"