aivika 0.4.2 → 0.4.3
raw patch · 8 files changed
+395/−166 lines, 8 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Simulation.Aivika.Statistics: AnalysisResults :: Array Int a -> Double -> Double -> a -> a -> AnalysisResults a
- Simulation.Aivika.Statistics: addStatistics :: MArray IOUArray a IO => Statistics a -> a -> IO ()
- Simulation.Aivika.Statistics: analyzeData :: Real a => Array Int a -> AnalysisResults a
- Simulation.Aivika.Statistics: data AnalysisResults a
- Simulation.Aivika.Statistics: data Statistics a
- Simulation.Aivika.Statistics: instance Eq a => Eq (AnalysisResults a)
- Simulation.Aivika.Statistics: instance Ord a => Ord (AnalysisResults a)
- Simulation.Aivika.Statistics: instance Show a => Show (AnalysisResults a)
- Simulation.Aivika.Statistics: newStatistics :: MArray IOUArray a IO => IO (Statistics a)
- Simulation.Aivika.Statistics: resultsData :: AnalysisResults a -> Array Int a
- Simulation.Aivika.Statistics: resultsMax :: AnalysisResults a -> a
- Simulation.Aivika.Statistics: resultsMean :: AnalysisResults a -> Double
- Simulation.Aivika.Statistics: resultsMin :: AnalysisResults a -> a
- Simulation.Aivika.Statistics: resultsVariance :: AnalysisResults a -> Double
- Simulation.Aivika.Statistics: showResults :: Show a => AnalysisResults a -> Int -> ShowS
- Simulation.Aivika.Statistics: statisticsData :: MArray IOUArray a IO => Statistics a -> IO (Array Int a)
+ Simulation.Aivika.Dynamics.Base: integIteration :: Dynamics Int
+ Simulation.Aivika.Dynamics.Base: integIterationBnds :: Specs -> (Int, Int)
+ Simulation.Aivika.Dynamics.Base: integIterationHiBnd :: Specs -> Int
+ Simulation.Aivika.Dynamics.Base: integIterationLoBnd :: Specs -> Int
+ Simulation.Aivika.Dynamics.Base: isTimeInteg :: Dynamics Bool
+ Simulation.Aivika.Dynamics.Signal: newSignalHistoryThrough :: EventQueue -> Signal a -> Dynamics (SignalHistory a)
+ Simulation.Aivika.Statistics: SamplingStats :: !Int -> !a -> !a -> !Double -> !Double -> SamplingStats a
+ Simulation.Aivika.Statistics: TimingStats :: !Int -> !a -> !a -> !Double -> !Double -> !Double -> !Double -> !Double -> !Double -> TimingStats a
+ Simulation.Aivika.Statistics: addSamplingStats :: SamplingData a => a -> SamplingStats a -> SamplingStats a
+ Simulation.Aivika.Statistics: addTimingStats :: TimingData a => Double -> a -> TimingStats a -> TimingStats a
+ Simulation.Aivika.Statistics: class SamplingData a
+ Simulation.Aivika.Statistics: class TimingData a
+ Simulation.Aivika.Statistics: data SamplingStats a
+ Simulation.Aivika.Statistics: data TimingStats a
+ Simulation.Aivika.Statistics: emptySamplingStats :: SamplingData a => SamplingStats a
+ Simulation.Aivika.Statistics: emptyTimingStats :: TimingData a => TimingStats a
+ Simulation.Aivika.Statistics: instance ConvertableToDouble Double
+ Simulation.Aivika.Statistics: instance ConvertableToDouble Int
+ Simulation.Aivika.Statistics: instance Eq a => Eq (SamplingStats a)
+ Simulation.Aivika.Statistics: instance Eq a => Eq (TimingStats a)
+ Simulation.Aivika.Statistics: instance Ord a => Ord (SamplingStats a)
+ Simulation.Aivika.Statistics: instance Ord a => Ord (TimingStats a)
+ Simulation.Aivika.Statistics: instance SamplingData Double
+ Simulation.Aivika.Statistics: instance SamplingData Int
+ Simulation.Aivika.Statistics: instance Show a => Show (SamplingStats a)
+ Simulation.Aivika.Statistics: instance Show a => Show (TimingStats a)
+ Simulation.Aivika.Statistics: instance TimingData Double
+ Simulation.Aivika.Statistics: instance TimingData Int
+ Simulation.Aivika.Statistics: samplingStatsCount :: SamplingStats a -> !Int
+ Simulation.Aivika.Statistics: samplingStatsDeviation :: SamplingStats a -> Double
+ Simulation.Aivika.Statistics: samplingStatsMax :: SamplingStats a -> !a
+ Simulation.Aivika.Statistics: samplingStatsMean :: SamplingStats a -> !Double
+ Simulation.Aivika.Statistics: samplingStatsMean2 :: SamplingStats a -> !Double
+ Simulation.Aivika.Statistics: samplingStatsMin :: SamplingStats a -> !a
+ Simulation.Aivika.Statistics: samplingStatsVariance :: SamplingStats a -> Double
+ Simulation.Aivika.Statistics: showSamplingStats :: Show a => SamplingStats a -> Int -> ShowS
+ Simulation.Aivika.Statistics: showTimingStats :: (Show a, TimingData a) => TimingStats a -> Int -> ShowS
+ Simulation.Aivika.Statistics: timingStatsCount :: TimingStats a -> !Int
+ Simulation.Aivika.Statistics: timingStatsDeviation :: TimingData a => TimingStats a -> Double
+ Simulation.Aivika.Statistics: timingStatsLastTime :: TimingStats a -> !Double
+ Simulation.Aivika.Statistics: timingStatsMax :: TimingStats a -> !a
+ Simulation.Aivika.Statistics: timingStatsMaxTime :: TimingStats a -> !Double
+ Simulation.Aivika.Statistics: timingStatsMean :: TimingData a => TimingStats a -> Double
+ Simulation.Aivika.Statistics: timingStatsMin :: TimingStats a -> !a
+ Simulation.Aivika.Statistics: timingStatsMinTime :: TimingStats a -> !Double
+ Simulation.Aivika.Statistics: timingStatsStartTime :: TimingStats a -> !Double
+ Simulation.Aivika.Statistics: timingStatsSum :: TimingStats a -> !Double
+ Simulation.Aivika.Statistics: timingStatsSum2 :: TimingStats a -> !Double
+ Simulation.Aivika.Statistics: timingStatsVariance :: TimingData a => TimingStats a -> Double
- Simulation.Aivika.Dynamics.Base: integTimes :: Simulation [Double]
+ Simulation.Aivika.Dynamics.Base: integTimes :: Specs -> [Double]
Files
- Simulation/Aivika/Dynamics/Base.hs +5/−0
- Simulation/Aivika/Dynamics/Internal/Dynamics.hs +33/−33
- Simulation/Aivika/Dynamics/Internal/Memo.hs +8/−8
- Simulation/Aivika/Dynamics/Internal/Time.hs +21/−8
- Simulation/Aivika/Dynamics/Signal.hs +30/−3
- Simulation/Aivika/Statistics.hs +281/−101
- aivika.cabal +1/−1
- examples/Furnace.hs +16/−12
Simulation/Aivika/Dynamics/Base.hs view
@@ -17,6 +17,11 @@ dt, time, integTimes,+ isTimeInteg,+ integIteration,+ integIterationBnds,+ integIterationLoBnd,+ integIterationHiBnd, -- * Interpolation and Initial Value initDynamics, discrete,
Simulation/Aivika/Dynamics/Internal/Dynamics.hs view
@@ -26,12 +26,12 @@ throwDynamics, -- * Utilities basicTime,- iterationBnds,- iterationHiBnd,- iterationLoBnd,- phaseBnds,- phaseHiBnd,- phaseLoBnd) where+ integIterationBnds,+ integIterationHiBnd,+ integIterationLoBnd,+ integPhaseBnds,+ integPhaseHiBnd,+ integPhaseLoBnd) where import qualified Control.Exception as C import Control.Exception (IOException, throw, finally)@@ -61,50 +61,50 @@ pointPhase :: Int -- ^ the current phase } deriving (Eq, Ord, Show) --- | Returns the iterations starting from zero.-iterations :: Specs -> [Int]-iterations sc = [i1 .. i2] where+-- | Returns the integration iterations starting from zero.+integIterations :: Specs -> [Int]+integIterations sc = [i1 .. i2] where i1 = 0 i2 = round ((spcStopTime sc - spcStartTime sc) / spcDT sc) --- | Returns the first and last iterations.-iterationBnds :: Specs -> (Int, Int)-iterationBnds sc = (0, round ((spcStopTime sc - - spcStartTime sc) / spcDT sc))+-- | Returns the first and last integration iterations.+integIterationBnds :: Specs -> (Int, Int)+integIterationBnds sc = (0, round ((spcStopTime sc - + spcStartTime sc) / spcDT sc)) --- | Returns the first iteration, i.e. zero.-iterationLoBnd :: Specs -> Int-iterationLoBnd sc = 0+-- | Returns the first integration iteration, i.e. zero.+integIterationLoBnd :: Specs -> Int+integIterationLoBnd sc = 0 --- | Returns the last iteration.-iterationHiBnd :: Specs -> Int-iterationHiBnd sc = round ((spcStopTime sc - - spcStartTime sc) / spcDT sc)+-- | Returns the last integration iteration.+integIterationHiBnd :: Specs -> Int+integIterationHiBnd sc = round ((spcStopTime sc - + spcStartTime sc) / spcDT sc) -- | Returns the phases for the specified simulation specs starting from zero.-phases :: Specs -> [Int]-phases sc = +integPhases :: Specs -> [Int]+integPhases sc = case spcMethod sc of Euler -> [0] RungeKutta2 -> [0, 1] RungeKutta4 -> [0, 1, 2, 3] --- | Returns the first and last phases.-phaseBnds :: Specs -> (Int, Int)-phaseBnds sc = +-- | Returns the first and last integration phases.+integPhaseBnds :: Specs -> (Int, Int)+integPhaseBnds sc = case spcMethod sc of Euler -> (0, 0) RungeKutta2 -> (0, 1) RungeKutta4 -> (0, 3) --- | Returns the first phase, i.e. zero.-phaseLoBnd :: Specs -> Int-phaseLoBnd sc = 0+-- | Returns the first integration phase, i.e. zero.+integPhaseLoBnd :: Specs -> Int+integPhaseLoBnd sc = 0 --- | Returns the last phase, 1 for Euler's method, 2 for RK2 and 4 for RK4.-phaseHiBnd :: Specs -> Int-phaseHiBnd sc = +-- | Returns the last integration phase, 0 for Euler's method, 1 for RK2 and 3 for RK4.+integPhaseHiBnd :: Specs -> Int+integPhaseHiBnd sc = case spcMethod sc of Euler -> 0 RungeKutta2 -> 1@@ -159,7 +159,7 @@ runDynamicsInStopTime (Dynamics m) = Simulation $ \r -> do let sc = runSpecs r - n = iterationHiBnd sc+ n = integIterationHiBnd sc t = basicTime sc n 0 m Point { pointSpecs = sc, pointRun = r,@@ -172,7 +172,7 @@ runDynamicsInIntegTimes (Dynamics m) = Simulation $ \r -> do let sc = runSpecs r- (nl, nu) = iterationBnds sc+ (nl, nu) = integIterationBnds sc point n = Point { pointSpecs = sc, pointRun = r, pointTime = basicTime sc n 0,
Simulation/Aivika/Dynamics/Internal/Memo.hs view
@@ -43,8 +43,8 @@ memo (Dynamics m) = Simulation $ \r -> do let sc = runSpecs r- (phl, phu) = phaseBnds sc- (nl, nu) = iterationBnds sc+ (phl, phu) = integPhaseBnds sc+ (nl, nu) = integIterationBnds sc arr <- newMemoArray_ ((phl, nl), (phu, nu)) nref <- newIORef 0 phref <- newIORef 0@@ -52,7 +52,7 @@ do let sc = pointSpecs p n = pointIteration p ph = pointPhase p- phu = phaseHiBnd sc + phu = integPhaseHiBnd sc loop n' ph' = if (n' > n) || ((n' == n) && (ph' > ph)) then @@ -80,8 +80,8 @@ umemo (Dynamics m) = Simulation $ \r -> do let sc = runSpecs r- (phl, phu) = phaseBnds sc- (nl, nu) = iterationBnds sc+ (phl, phu) = integPhaseBnds sc+ (nl, nu) = integIterationBnds sc arr <- newMemoUArray_ ((phl, nl), (phu, nu)) nref <- newIORef 0 phref <- newIORef 0@@ -89,7 +89,7 @@ do let sc = pointSpecs p n = pointIteration p ph = pointPhase p- phu = phaseHiBnd sc + phu = integPhaseHiBnd sc loop n' ph' = if (n' > n) || ((n' == n) && (ph' > ph)) then @@ -122,7 +122,7 @@ memo0 (Dynamics m) = Simulation $ \r -> do let sc = runSpecs r- bnds = iterationBnds sc+ bnds = integIterationBnds sc arr <- newMemoArray_ bnds nref <- newIORef 0 let r p =@@ -150,7 +150,7 @@ umemo0 (Dynamics m) = Simulation $ \r -> do let sc = runSpecs r- bnds = iterationBnds sc+ bnds = integIterationBnds sc arr <- newMemoUArray_ bnds nref <- newIORef 0 let r p =
Simulation/Aivika/Dynamics/Internal/Time.hs view
@@ -15,7 +15,10 @@ stoptime, dt, time,- integTimes) where+ integTimes, + isTimeInteg,+ integIteration,+ integPhase) where import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics@@ -37,10 +40,20 @@ time = Dynamics $ return . pointTime -- | Return the integration time points.-integTimes :: Simulation [Double]-integTimes =- Simulation $ \r ->- do let sc = runSpecs r- (nl, nu) = iterationBnds sc- t n = basicTime sc n 0- return $ map t [nl .. nu]+integTimes :: Specs -> [Double]+integTimes sc = map t [nl .. nu]+ where (nl, nu) = integIterationBnds sc+ t n = basicTime sc n 0+ +-- | Whether the current time is an integration time.+isTimeInteg :: Dynamics Bool+isTimeInteg = Dynamics $ \p -> return $ pointPhase p >= 0++-- | Return the integration iteration closest to the current simulation time.+integIteration :: Dynamics Int+integIteration = Dynamics $ return . pointIteration++-- | Return the integration phase for the current simulation time.+-- It is @(-1)@ for non-integration time points.+integPhase :: Dynamics Int+integPhase = Dynamics $ return . pointPhase
Simulation/Aivika/Dynamics/Signal.hs view
@@ -40,6 +40,7 @@ SignalHistory, signalHistorySignal, newSignalHistory,+ newSignalHistoryThrough, readSignalHistory) where import Data.IORef@@ -54,6 +55,7 @@ import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.Internal.Cont import Simulation.Aivika.Dynamics.Internal.Process+import Simulation.Aivika.Dynamics.Base import qualified Simulation.Aivika.Vector as V import qualified Simulation.Aivika.UVector as UV@@ -105,6 +107,31 @@ signalHistoryTimes = ts, signalHistoryValues = xs } +-- | Create a history of the signal values with delay through the event queue.+-- The history will be created at the same simulation time, just the corresponded +-- handler will be subscribed to the signal after the new event will be processed +-- by the queue. +-- +-- It is very useful if we want the signal won't be triggered at the current +-- time until we complete some preparation. This is relatated to the fact that+-- the signal is updated at time of subscribing the handler. So, if we subscribe+-- to the signal which must be triggered at the current time then it will be+-- triggered. Using the event queue allows us to complete some preparation logic+-- before the signal will be triggered at the same simulation time point.+newSignalHistoryThrough :: EventQueue -> Signal a -> Dynamics (SignalHistory a)+newSignalHistoryThrough q signal =+ do ts <- liftIO UV.newVector+ xs <- liftIO V.newVector+ t <- time+ enqueue q t $+ handleSignal_ signal $ \a ->+ Dynamics $ \p ->+ do liftIO $ UV.appendVector ts (pointTime p)+ liftIO $ V.appendVector xs a+ return SignalHistory { signalHistorySignal = signal,+ signalHistoryTimes = ts,+ signalHistoryValues = xs }+ -- | Read the history of signal values. readSignalHistory :: SignalHistory a -> Dynamics (Array Int Double, Array Int a) readSignalHistory history =@@ -144,7 +171,7 @@ newSignalInIntegTimes q = Dynamics $ \p -> do let sc = pointSpecs p- (nl, nu) = iterationBnds sc+ (nl, nu) = integIterationBnds sc point n = Point { pointSpecs = sc, pointRun = pointRun p, pointTime = basicTime sc n 0,@@ -159,7 +186,7 @@ newSignalInStartTime q = Dynamics $ \p -> do let sc = pointSpecs p- (nl, nu) = iterationBnds sc+ (nl, nu) = integIterationBnds sc point n = Point { pointSpecs = sc, pointRun = pointRun p, pointTime = basicTime sc n 0,@@ -173,7 +200,7 @@ newSignalInStopTime q = Dynamics $ \p -> do let sc = pointSpecs p- (nl, nu) = iterationBnds sc+ (nl, nu) = integIterationBnds sc point n = Point { pointSpecs = sc, pointRun = pointRun p, pointTime = basicTime sc n 0,
Simulation/Aivika/Statistics.hs view
@@ -1,6 +1,4 @@ -{-# LANGUAGE FlexibleContexts #-}- -- | -- Module : Simulation.Aivika.Statistics -- Copyright : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com>@@ -9,111 +7,293 @@ -- Stability : experimental -- Tested with: GHC 7.0.3 ----- Represents statistics and results.+-- Represents statistics. --+ module Simulation.Aivika.Statistics- (Statistics, - newStatistics,- addStatistics,- statisticsData,- analyzeData,- AnalysisResults(..),- showResults) where + (SamplingStats(..),+ SamplingData(..),+ samplingStatsVariance,+ samplingStatsDeviation,+ showSamplingStats,+ TimingStats(..),+ TimingData(..),+ timingStatsDeviation,+ showTimingStats) where -import Data.Foldable-import Data.Array-import Data.Array.IO-import Control.Monad-import Control.Monad.Trans-import Control.Concurrent.MVar+-- | Defines data types that can be converted to 'Double'.+class Ord a => ConvertableToDouble a where+ + -- | Convert the value to 'Double'.+ convertToDouble :: a -> Double+ +instance ConvertableToDouble Double where+ convertToDouble = id+ +instance ConvertableToDouble Int where+ convertToDouble = fromInteger . toInteger -import Simulation.Aivika.UVector+-- | Describes when the statistics consists of only samples +-- not bound to the simulation time.+data SamplingStats a = + SamplingStats { samplingStatsCount :: !Int,+ -- ^ The total number of samples.+ samplingStatsMin :: !a,+ -- ^ The minimum value among the samples.+ samplingStatsMax :: !a,+ -- ^ The maximum value among the samples.+ samplingStatsMean :: !Double,+ -- ^ The average value.+ samplingStatsMean2 :: !Double + -- ^ The average square value.+ }+ deriving (Eq, Ord, Show)+ +-- | Specifies data type from which values we can gather the statistics. +class SamplingData a where + + -- | An empty statistics that has no samples. + emptySamplingStats :: SamplingStats a+ + -- | Add a new sample to the statistics.+ addSamplingStats :: a -> SamplingStats a -> SamplingStats a --- | 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 () }+instance SamplingData Double where --- | Create new statistics.-newStatistics :: (MArray IOUArray a IO) => IO (Statistics a)-newStatistics = - do v <- newVector- l <- newMVar ()- return Statistics { statData = v, - statLock = l }+ emptySamplingStats =+ SamplingStats { samplingStatsCount = 0,+ samplingStatsMin = 1 / 0,+ samplingStatsMax = (-1) / 0,+ samplingStatsMean = 0 / 0,+ samplingStatsMean2 = 0 / 0 }+ + addSamplingStats = addSamplingStatsGeneric+ +instance SamplingData Int where --- | 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+ emptySamplingStats =+ SamplingStats { samplingStatsCount = 0,+ samplingStatsMin = maxBound,+ samplingStatsMax = minBound,+ samplingStatsMean = 0 / 0,+ samplingStatsMean2 = 0 / 0 }+ + addSamplingStats = addSamplingStatsGeneric+ +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 = fromInteger $ toInteger count+ x = convertToDouble a+ k1 = 1.0 / n+ k2 = (n - 1.0) / n --- | 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)+-- | Return the variance.+samplingStatsVariance :: SamplingStats a -> Double+samplingStatsVariance stats+ | count == 1 = meanX2 - meanX * meanX+ | otherwise = (meanX2 - meanX * meanX) * (n / (n - 1))+ where count = samplingStatsCount stats+ meanX = samplingStatsMean stats+ meanX2 = samplingStatsMean2 stats+ n = fromInteger $ toInteger count+ +-- | Return the deviation. +samplingStatsDeviation :: SamplingStats a -> Double+samplingStatsDeviation = sqrt . samplingStatsVariance --- | 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 = ---"+-- | Show the summary of the statistics with the specified indent. +showSamplingStats :: (Show a) => SamplingStats a -> Int -> ShowS+showSamplingStats 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 "deviation = " . shows (samplingStatsDeviation stats) . + showString "\n" .+ showString tab .+ showString "minimum = " . shows (samplingStatsMin stats) . + showString "\n" .+ showString tab .+ showString "maximum = " . shows (samplingStatsMax stats)+ +-- | This is the timing statistics where data are bound to the time.+data TimingStats a =+ TimingStats { timingStatsCount :: !Int,+ -- ^ Return the number of samples.+ timingStatsMin :: !a,+ -- ^ Return the minimum value.+ timingStatsMax :: !a,+ -- ^ Return the maximum value.+ timingStatsMinTime :: !Double,+ -- ^ Return the time at which the minimum is attained.+ timingStatsMaxTime :: !Double,+ -- ^ Return the time at which the maximum is attained.+ timingStatsStartTime :: !Double,+ -- ^ Return the start time of sampling.+ timingStatsLastTime :: !Double,+ -- ^ Return the last time of sampling.+ timingStatsSum :: !Double,+ -- ^ Return the sum of values.+ timingStatsSum2 :: !Double + -- ^ Return the sum of square values.+ } deriving (Eq, Ord, Show)+ +-- | Defines the data type from which values we can gather the timing statistics.+class TimingData a where + + -- | An empty statistics that has no samples.+ emptyTimingStats :: TimingStats a+ + -- | Add a sample with the specified time to the statistics.+ addTimingStats :: Double -> a -> TimingStats a -> TimingStats a+ + -- | Return the average value.+ timingStatsMean :: TimingStats a -> Double+ + -- | Return the variance.+ 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,+ timingStatsSum2 = 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,+ timingStatsSum2 = 0 }+ + addTimingStats = addTimingStatsGeneric+ timingStatsMean = timingStatsMeanGeneric+ timingStatsVariance = timingStatsVarianceGeneric++addTimingStatsGeneric :: ConvertableToDouble a => Double -> a -> TimingStats a -> TimingStats a+addTimingStatsGeneric t a stats+ | isNaN x = stats+ | count == 1 = TimingStats { timingStatsCount = 1,+ timingStatsMin = a,+ timingStatsMax = a,+ timingStatsMinTime = t,+ timingStatsMaxTime = t,+ timingStatsStartTime = t,+ timingStatsLastTime = t,+ timingStatsSum = 0,+ timingStatsSum2 = 0 }+ | t < t' = error "The current time cannot be less than the previous one: addTimingStats"+ | 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 + | t1 > t0 = sumX / (t1 - t0)+ | otherwise = minX+ where t0 = timingStatsStartTime stats+ t1 = timingStatsLastTime stats+ sumX = timingStatsSum stats+ minX = convertToDouble $ timingStatsMin stats+ +timingStatsMean2Generic :: ConvertableToDouble a => TimingStats a -> Double+timingStatsMean2Generic stats+ | t1 > t0 = sumX2 / (t1 - t0)+ | otherwise = minX * minX+ where t0 = timingStatsStartTime stats+ t1 = timingStatsLastTime stats+ sumX2 = timingStatsSum2 stats+ minX = convertToDouble $ timingStatsMin stats++timingStatsVarianceGeneric :: ConvertableToDouble a => TimingStats a -> Double+timingStatsVarianceGeneric stats = ex2 - ex * ex+ where ex = timingStatsMeanGeneric stats+ ex2 = timingStatsMean2Generic stats+ +-- | Return the deviation. +timingStatsDeviation :: TimingData a => TimingStats a -> Double+timingStatsDeviation = sqrt . timingStatsVariance++-- | Show the summary of the statistics with the specified indent. +showTimingStats :: (Show a, TimingData a) => TimingStats a -> Int -> ShowS+showTimingStats 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 "deviation = " . shows (timingStatsDeviation stats) . + showString "\n" .+ showString tab .+ showString "minimum = " . shows (timingStatsMin stats) . + showString " at t = " . shows (timingStatsMinTime stats) .+ showString "\n" .+ showString tab .+ showString "maximum = " . shows (timingStatsMax stats) .+ showString " at t = " . shows (timingStatsMaxTime stats) .+ showString "\n" .+ showString "t in [" . shows (timingStatsStartTime stats) .+ showString ", " . shows (timingStatsLastTime stats) .+ showString "]"
aivika.cabal view
@@ -1,5 +1,5 @@ name: aivika-version: 0.4.2+version: 0.4.3 synopsis: A multi-paradigm simulation library description: Aivika is a small simulation library that covers many paradigms.
examples/Furnace.hs view
@@ -46,13 +46,13 @@ -- ^ The pits for ingots. furnacePitCount :: UVar Int, -- ^ The count of active pits with ingots.- furnacePitCountStats :: Statistics Int,+ furnacePitCountStats :: Ref (SamplingStats Int), -- ^ The statistics about the active pits. furnaceAwaitingIngots :: Q.Queue Ingot, -- ^ The awaiting ingots in the queue. furnaceQueueCount :: UVar Int, -- ^ The queue count.- furnaceQueueCountStats :: Statistics Int,+ furnaceQueueCountStats :: Ref (SamplingStats Int), -- ^ The statistics about the queue count. furnaceWaitCount :: Ref Int, -- ^ The count of awaiting ingots.@@ -103,10 +103,10 @@ do normalGen <- liftIO normalGen pits <- sequence [newPit queue | i <- [1..10]] pitCount <- newUVar queue 0- pitCountStats <- liftIO newStatistics+ pitCountStats <- newRef queue emptySamplingStats awaitingIngots <- liftIO Q.newQueue queueCount <- newUVar queue 0- queueCountStats <- liftIO newStatistics+ queueCountStats <- newRef queue emptySamplingStats waitCount <- newRef queue 0 waitTime <- newRef queue 0.0 heatingTime <- newRef queue 0.0@@ -198,7 +198,8 @@ t' <- time modifyUVar (furnaceQueueCount furnace) (+ (-1)) c <- readUVar (furnaceQueueCount furnace)- liftIO $ addStatistics (furnaceQueueCountStats furnace) c+ modifyRef (furnaceQueueCountStats furnace) $+ addSamplingStats c loadIngot (ingot { ingotLoadTime = t', ingotLoadTemp = 400.0 }) pit @@ -213,7 +214,8 @@ let furnace = ingotFurnace ingot count <- readUVar (furnacePitCount furnace) writeUVar (furnacePitCount furnace) (count - 1)- liftIO $ addStatistics (furnacePitCountStats furnace) (count - 1)+ modifyRef (furnacePitCountStats furnace) $+ addSamplingStats (count - 1) -- how long did we heat the ingot up? t' <- time@@ -236,7 +238,8 @@ let furnace = ingotFurnace ingot count <- readUVar (furnacePitCount furnace) writeUVar (furnacePitCount furnace) (count + 1)- liftIO $ addStatistics (furnacePitCountStats furnace) (count + 1)+ modifyRef (furnacePitCountStats furnace) $+ addSamplingStats (count + 1) -- decrease the furnace temperature h <- readRef (furnaceTemp furnace)@@ -293,7 +296,8 @@ liftIO $ Q.enqueue ingots ingot modifyUVar (furnaceQueueCount furnace) (+ 1) c <- readUVar (furnaceQueueCount furnace)- liftIO $ addStatistics (furnaceQueueCountStats furnace) c+ modifyRef (furnaceQueueCountStats furnace) $+ addSamplingStats c else do pit:_ <- emptyPits furnace loadIngot ingot pit @@ -384,19 +388,19 @@ putStrLn "" -- the ingots in pits- r2 <- fmap analyzeData $ liftIO $ statisticsData (furnacePitCountStats furnace)+ r2 <- readRef (furnacePitCountStats furnace) liftIO $ do putStrLn "The ingots in pits: "- putStrLn $ showResults r2 2 []+ putStrLn $ showSamplingStats r2 2 [] putStrLn "" -- the queue size- r3 <- fmap analyzeData $ liftIO $ statisticsData (furnaceQueueCountStats furnace)+ r3 <- readRef (furnaceQueueCountStats furnace) liftIO $ do putStrLn "The queue size: "- putStrLn $ showResults r3 2 []+ putStrLn $ showSamplingStats r3 2 [] putStrLn "" -- the mean wait time in the queue