aivika 1.0 → 1.1
raw patch · 38 files changed
+1453/−773 lines, 38 files
Files
- Simulation/Aivika.hs +5/−2
- Simulation/Aivika/Arrival.hs +72/−0
- Simulation/Aivika/Dynamics/Memo.hs +3/−1
- Simulation/Aivika/Dynamics/Memo/Unboxed.hs +3/−1
- Simulation/Aivika/Dynamics/Random.hs +10/−1
- Simulation/Aivika/Event.hs +1/−0
- Simulation/Aivika/Internal/Event.hs +31/−21
- Simulation/Aivika/Internal/Parameter.hs +7/−1
- Simulation/Aivika/Internal/Process.hs +25/−22
- Simulation/Aivika/Internal/Signal.hs +20/−6
- Simulation/Aivika/Internal/Specs.hs +27/−7
- Simulation/Aivika/Parameter.hs +3/−0
- Simulation/Aivika/Parameter/Random.hs +28/−55
- Simulation/Aivika/Process.hs +2/−2
- Simulation/Aivika/Processor.hs +58/−34
- Simulation/Aivika/Queue.hs +228/−228
- Simulation/Aivika/Queue/Infinite.hs +133/−133
- Simulation/Aivika/Server.hs +155/−137
- Simulation/Aivika/Signal.hs +1/−0
- Simulation/Aivika/Statistics/Accumulator.hs +44/−0
- Simulation/Aivika/Stream.hs +41/−5
- Simulation/Aivika/Stream/Random.hs +74/−42
- Simulation/Aivika/Task.hs +1/−1
- Simulation/Aivika/Var.hs +4/−5
- Simulation/Aivika/Var/Unboxed.hs +4/−5
- aivika.cabal +27/−31
- examples/BassDiffusion.hs +2/−7
- examples/Furnace.hs +6/−6
- examples/MachRep1.hs +3/−3
- examples/MachRep1EventDriven.hs +2/−2
- examples/MachRep1TimeDriven.hs +2/−2
- examples/MachRep2.hs +3/−3
- examples/MachRep3.hs +3/−3
- examples/SimpleWorkflow.hs +157/−0
- examples/TimeOut.hs +4/−4
- examples/TimeOutInt.hs +3/−3
- examples/TimeOutWait.hs +70/−0
- examples/WorkflowLoop.hs +191/−0
Simulation/Aivika.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Simulation.Aivika--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- Copyright : Copyright (c) 2009-2014, David Sorokin <david.sorokin@gmail.com> -- License : BSD3 -- Maintainer : David Sorokin <david.sorokin@gmail.com> -- Stability : experimental@@ -13,6 +13,7 @@ module Simulation.Aivika (-- * Modules module Simulation.Aivika.Agent,+ module Simulation.Aivika.Arrival, module Simulation.Aivika.Cont, module Simulation.Aivika.Dynamics, module Simulation.Aivika.Dynamics.Interpolate,@@ -33,12 +34,14 @@ module Simulation.Aivika.Simulation, module Simulation.Aivika.Specs, module Simulation.Aivika.Statistics,+ module Simulation.Aivika.Statistics.Accumulator, module Simulation.Aivika.Stream, module Simulation.Aivika.Stream.Random, module Simulation.Aivika.Task, module Simulation.Aivika.Var.Unboxed) where import Simulation.Aivika.Agent+import Simulation.Aivika.Arrival import Simulation.Aivika.Cont import Simulation.Aivika.Dynamics import Simulation.Aivika.Dynamics.Interpolate@@ -59,8 +62,8 @@ import Simulation.Aivika.Simulation import Simulation.Aivika.Specs import Simulation.Aivika.Statistics+import Simulation.Aivika.Statistics.Accumulator import Simulation.Aivika.Stream import Simulation.Aivika.Stream.Random import Simulation.Aivika.Task import Simulation.Aivika.Var.Unboxed-
+ Simulation/Aivika/Arrival.hs view
@@ -0,0 +1,72 @@++-- |+-- Module : Simulation.Aivika.Arrival+-- Copyright : Copyright (c) 2009-2014, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- This module defines the types and functions for working with+-- the external events that usually arrive from outside the model.++module Simulation.Aivika.Arrival+ (Arrival(..),+ ArrivalTimer,+ newArrivalTimer,+ arrivalTimerProcessor,+ arrivalProcessingTime) where++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Simulation+import Simulation.Aivika.Dynamics+import Simulation.Aivika.Event+import Simulation.Aivika.Processor+import Simulation.Aivika.Stream+import Simulation.Aivika.Statistics+import Simulation.Aivika.Ref++-- | It defines when an external event has arrived, usually generated by+-- some random 'Stream'.+--+-- Such events should arrive one by one without time lag in the following sense+-- that the model should start awaiting the next event exactly in that time+-- when the previous event has arrived. It usually happens automatically when+-- using a 'queueProcessor' with an ability to lost the arrived event if the queue+-- is full.+data Arrival =+ Arrival { arrivalTime :: Double,+ -- ^ the simulation time at which the event has arrived+ arrivalDelay :: Double+ -- ^ the delay time which has passed from the time of+ -- arriving the previous event+ } deriving (Eq, Ord, Show)++-- | Accumulates the statistics about that how long the arrived events are processed.+data ArrivalTimer =+ ArrivalTimer { arrivalProcessingTimeRef :: Ref (SamplingStats Double) }++-- | Create a new timer that measures how long the arrived events are processed.+newArrivalTimer :: Simulation ArrivalTimer+newArrivalTimer =+ do r <- newRef emptySamplingStats+ return ArrivalTimer { arrivalProcessingTimeRef = r }++-- | Return the statistics about that how long the arrived events were processed.+arrivalProcessingTime :: ArrivalTimer -> Event (SamplingStats Double)+arrivalProcessingTime = readRef . arrivalProcessingTimeRef++-- | Return a processor that actually measures how much time has passed from+-- the time of arriving the events.+arrivalTimerProcessor :: ArrivalTimer -> Processor Arrival Arrival+arrivalTimerProcessor timer =+ Processor $ \xs -> Cons $ loop xs where+ loop xs =+ do (a, xs) <- runStream xs+ liftEvent $+ do t <- liftDynamics time+ modifyRef (arrivalProcessingTimeRef timer) $+ addSamplingStats (t - arrivalTime a)+ return (a, Cons $ loop xs)
Simulation/Aivika/Dynamics/Memo.hs view
@@ -23,6 +23,7 @@ import Control.Monad import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Parameter import Simulation.Aivika.Internal.Simulation import Simulation.Aivika.Internal.Dynamics import Simulation.Aivika.Dynamics.Interpolate@@ -32,7 +33,8 @@ newBoxedArray_ = newArray_ -- | Memoize and order the computation in the integration time points using --- the interpolation that knows of the Runge-Kutta method.+-- the interpolation that knows of the Runge-Kutta method. The values are+-- calculated sequentially starting from 'starttime'. memoDynamics :: Dynamics e -> Simulation (Dynamics e) {-# INLINE memoDynamics #-} memoDynamics (Dynamics m) =
Simulation/Aivika/Dynamics/Memo/Unboxed.hs view
@@ -24,13 +24,15 @@ import Control.Monad import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Parameter import Simulation.Aivika.Internal.Simulation import Simulation.Aivika.Internal.Dynamics import Simulation.Aivika.Dynamics.Interpolate import Simulation.Aivika.Unboxed -- | Memoize and order the computation in the integration time points using --- the interpolation that knows of the Runge-Kutta method.+-- the interpolation that knows of the Runge-Kutta method. The values are+-- calculated sequentially starting from 'starttime'. memoDynamics :: Unboxed e => Dynamics e -> Simulation (Dynamics e) {-# INLINE memoDynamics #-} memoDynamics (Dynamics m) =
Simulation/Aivika/Dynamics/Random.hs view
@@ -7,7 +7,16 @@ -- Stability : experimental -- Tested with: GHC 7.6.3 ----- This module defines the random parameters of simulation experiments.+-- This module defines the random functions that always return the same values+-- in the integration time points within a single simulation run. The values+-- for another simulation run will be regenerated anew.+--+-- For example, the computations returned by these functions can be used in+-- the equations of System Dynamics.+--+-- Also it is worth noting that the values are generated in a strong order starting+-- from 'starttime' with step 'dt'. This is how the 'memo0Dynamics' function+-- actually works. -- module Simulation.Aivika.Dynamics.Random
Simulation/Aivika/Event.hs view
@@ -16,6 +16,7 @@ EventLift(..), EventProcessing(..), runEvent,+ runEventWith, runEventInStartTime, runEventInStopTime, -- * Event Queue
Simulation/Aivika/Internal/Event.hs view
@@ -19,6 +19,7 @@ EventProcessing(..), invokeEvent, runEvent,+ runEventWith, runEventInStartTime, runEventInStopTime, -- * Event Queue@@ -148,21 +149,23 @@ do { rec { a <- invokeEvent p (f a) }; return a } -- | Defines how the events are processed.-data EventProcessing = IncludingCurrentEvents+data EventProcessing = CurrentEvents -- ^ either process all earlier and then current events, -- or raise an error if the current simulation time is less- -- than the actual time of the event queue- | IncludingEarlierEvents+ -- than the actual time of the event queue (safe within+ -- the 'Event' computation as this is protected by the type system)+ | EarlierEvents -- ^ either process all earlier events not affecting -- the events at the current simulation time, -- or raise an error if the current simulation time is less- -- than the actual time of the event queue- | IncludingCurrentEventsOrFromPast+ -- than the actual time of the event queue (safe within+ -- the 'Event' computation as this is protected by the type system)+ | CurrentEventsOrFromPast -- ^ either process all earlier and then current events, -- or do nothing if the current simulation time is less -- than the actual time of the event queue -- (do not use unless the documentation states the opposite)- | IncludingEarlierEventsOrFromPast+ | EarlierEventsOrFromPast -- ^ either process all earlier events, -- or do nothing if the current simulation time is less -- than the actual time of the event queue@@ -248,28 +251,35 @@ -- | Process the events. processEvents :: EventProcessing -> Dynamics ()-processEvents IncludingCurrentEvents = processEventsIncludingCurrent-processEvents IncludingEarlierEvents = processEventsIncludingEarlier-processEvents IncludingCurrentEventsOrFromPast = processEventsIncludingCurrentCore-processEvents IncludingEarlierEventsOrFromPast = processEventsIncludingEarlierCore+processEvents CurrentEvents = processEventsIncludingCurrent+processEvents EarlierEvents = processEventsIncludingEarlier+processEvents CurrentEventsOrFromPast = processEventsIncludingCurrentCore+processEvents EarlierEventsOrFromPast = processEventsIncludingEarlierCore -- | Run the 'Event' computation in the current simulation time--- within the 'Dynamics' computation.-runEvent :: EventProcessing -> Event a -> Dynamics a-runEvent processing (Event e) =+-- within the 'Dynamics' computation involving all pending+-- 'CurrentEvents' in the processing too.+runEvent :: Event a -> Dynamics a+runEvent = runEventWith CurrentEvents++-- | Run the 'Event' computation in the current simulation time+-- within the 'Dynamics' computation specifying what pending events +-- should be involved in the processing.+runEventWith :: EventProcessing -> Event a -> Dynamics a+runEventWith processing (Event e) = Dynamics $ \p -> do invokeDynamics p $ processEvents processing e p --- | Run the 'Event' computation in the start time.-runEventInStartTime :: EventProcessing -> Event a -> Simulation a-runEventInStartTime processing e =- runDynamicsInStartTime $ runEvent processing e+-- | Run the 'Event' computation in the start time involving all+-- pending 'CurrentEvents' in the processing too.+runEventInStartTime :: Event a -> Simulation a+runEventInStartTime = runDynamicsInStartTime . runEvent --- | Run the 'Event' computation in the stop time.-runEventInStopTime :: EventProcessing -> Event a -> Simulation a-runEventInStopTime processing e =- runDynamicsInStopTime $ runEvent processing e+-- | Run the 'Event' computation in the stop time involving all+-- pending 'CurrentEvents' in the processing too.+runEventInStopTime :: Event a -> Simulation a+runEventInStopTime = runDynamicsInStopTime . runEvent -- | Return the number of pending events that should -- be yet actuated.
Simulation/Aivika/Internal/Parameter.hs view
@@ -12,6 +12,9 @@ -- The module defines the 'Parameter' monad that allows representing the model -- parameters. For example, they can be used when running the Monte-Carlo simulation. -- +-- In general, this monad is very useful for representing a computation which is external+-- relative to the model itself.+-- module Simulation.Aivika.Internal.Parameter (-- * Parameter Parameter(..),@@ -52,7 +55,10 @@ import Simulation.Aivika.Internal.Specs -- | The 'Parameter' monad that allows specifying the model parameters.--- For example, they can be used when running the Monte-Carlo simulation.+-- For example, they can be used when running the Monte-Carlo simulation.+-- +-- In general, this monad is very useful for representing a computation which is external+-- relative to the model itself. newtype Parameter a = Parameter (Run -> IO a) instance Monad Parameter where
Simulation/Aivika/Internal/Process.hs view
@@ -47,7 +47,7 @@ passivateProcess, processPassive, reactivateProcess,- cancelProcessUsingId,+ cancelProcessWithId, cancelProcess, processCancelled, -- * Awaiting Signal@@ -225,25 +225,28 @@ ccont = return m = invokeProcess pid p --- | Run the process in the start time immediately.-runProcessInStartTime :: EventProcessing -> Process () -> Simulation ()-runProcessInStartTime processing p =- runEventInStartTime processing $ runProcess p+-- | Run the process in the start time immediately involving all pending+-- 'CurrentEvents' in the computation too.+runProcessInStartTime :: Process () -> Simulation ()+runProcessInStartTime = runEventInStartTime . runProcess --- | Run the process in the start time immediately using the specified identifier.-runProcessInStartTimeUsingId :: EventProcessing -> ProcessId -> Process () -> Simulation ()-runProcessInStartTimeUsingId processing pid p =- runEventInStartTime processing $ runProcessUsingId pid p+-- | Run the process in the start time immediately using the specified identifier+-- and involving all pending 'CurrentEvents' in the computation too.+runProcessInStartTimeUsingId :: ProcessId -> Process () -> Simulation ()+runProcessInStartTimeUsingId pid p =+ runEventInStartTime $ runProcessUsingId pid p --- | Run the process in the final simulation time immediately.-runProcessInStopTime :: EventProcessing -> Process () -> Simulation ()-runProcessInStopTime processing p =- runEventInStopTime processing $ runProcess p+-- | Run the process in the final simulation time immediately involving all+-- pending 'CurrentEvents' in the computation too.+runProcessInStopTime :: Process () -> Simulation ()+runProcessInStopTime = runEventInStopTime . runProcess --- | Run the process in the final simulation time immediately using the specified identifier.-runProcessInStopTimeUsingId :: EventProcessing -> ProcessId -> Process () -> Simulation ()-runProcessInStopTimeUsingId processing pid p =- runEventInStopTime processing $ runProcessUsingId pid p+-- | Run the process in the final simulation time immediately using +-- the specified identifier and involving all pending 'CurrentEvents'+-- in the computation too.+runProcessInStopTimeUsingId :: ProcessId -> Process () -> Simulation ()+runProcessInStopTimeUsingId pid p =+ runEventInStopTime $ runProcessUsingId pid p -- | Enqueue the process that will be then started at the specified time -- from the event queue.@@ -278,15 +281,15 @@ processInterruptVersion = v } -- | Cancel a process with the specified identifier, interrupting it if needed.-cancelProcessUsingId :: ProcessId -> Event ()-cancelProcessUsingId pid = contCancellationInitiate (processCancelSource pid)+cancelProcessWithId :: ProcessId -> Event ()+cancelProcessWithId pid = contCancellationInitiate (processCancelSource pid) -- | The process cancels itself. cancelProcess :: Process a cancelProcess = do pid <- processId- liftEvent $ cancelProcessUsingId pid- throwProcess $ error "The process must be cancelled already: cancelProcessItself."+ liftEvent $ cancelProcessWithId pid+ throwProcess $ error "The process must be cancelled already: cancelProcess." -- | Test whether the process with the specified identifier was cancelled. processCancelled :: ProcessId -> Event Bool@@ -558,7 +561,7 @@ finallyProcess (holdProcess timeout) (liftEvent $- cancelProcessUsingId pid)+ cancelProcessWithId pid) spawnProcessUsingId CancelChildAfterParent pid $ do r <- liftIO $ newIORef Nothing finallyProcess
Simulation/Aivika/Internal/Signal.hs view
@@ -41,6 +41,7 @@ SignalHistory, signalHistorySignal, newSignalHistory,+ newSignalHistoryStartingWith, readSignalHistory, -- * Signalable Computations Signalable(..),@@ -251,13 +252,26 @@ -- | Create a history of the signal values. newSignalHistory :: Signal a -> Event (SignalHistory a)-newSignalHistory signal =- do ts <- liftIO UV.newVector- xs <- liftIO V.newVector- handleSignal_ signal $ \a ->+newSignalHistory =+ newSignalHistoryStartingWith Nothing++-- | Create a history of the signal values starting with+-- the optional initial value.+newSignalHistoryStartingWith :: Maybe a -> Signal a -> Event (SignalHistory a)+newSignalHistoryStartingWith init signal =+ Event $ \p ->+ do ts <- UV.newVector+ xs <- V.newVector+ case init of+ Nothing -> return ()+ Just a ->+ do UV.appendVector ts (pointTime p)+ V.appendVector xs a+ invokeEvent p $+ handleSignal_ signal $ \a -> Event $ \p ->- do liftIO $ UV.appendVector ts (pointTime p)- liftIO $ V.appendVector xs a+ do UV.appendVector ts (pointTime p)+ V.appendVector xs a return SignalHistory { signalHistorySignal = signal, signalHistoryTimes = ts, signalHistoryValues = xs }
Simulation/Aivika/Internal/Specs.hs view
@@ -86,14 +86,14 @@ -- | 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)+ i1 = integIterationLoBnd sc+ i2 = integIterationHiBnd sc -- | Returns the first and last integration iterations. integIterationBnds :: Specs -> (Int, Int)-integIterationBnds sc = (0, round ((spcStopTime sc - - spcStartTime sc) / spcDT sc))+integIterationBnds sc = (i1, i2) where+ i1 = integIterationLoBnd sc+ i2 = integIterationHiBnd sc -- | Returns the first integration iteration, i.e. zero. integIterationLoBnd :: Specs -> Int@@ -101,8 +101,28 @@ -- | Returns the last integration iteration. integIterationHiBnd :: Specs -> Int-integIterationHiBnd sc = round ((spcStopTime sc - - spcStartTime sc) / spcDT sc)+integIterationHiBnd sc =+ let n = round ((spcStopTime sc - + spcStartTime sc) / spcDT sc)+ in if n < 0+ then+ error $+ "The iteration number in the stop time has a negative value. " +++ "Either the simulation specs are incorrect, " +++ "or a floating point overflow occurred, " +++ "for example, when using a too small integration time step. " +++ "You have to define this time step regardless of " +++ "whether you actually use it or not, " +++ "for Aivika allows combining the ordinary differential equations " +++ "with the discrete event simulation within one model. " +++ "So, if you are still using the 32-bit architecture and " +++ "you do need a small integration time step " +++ "for integrating the equations " +++ "then you might think of using the 64-bit architecture. " +++ "Although you could probably just forget " +++ "to increase the time step " +++ "after increasing the stop time: integIterationHiBnd"+ else n -- | Returns the phases for the specified simulation specs starting from zero. integPhases :: Specs -> [Int]
Simulation/Aivika/Parameter.hs view
@@ -8,6 +8,9 @@ -- -- The module defines the 'Parameter' monad that allows representing the model -- parameters. For example, they can be used when running the Monte-Carlo simulation.+--+-- In general, this monad is very useful for representing a computation which is external+-- relative to the model itself. -- module Simulation.Aivika.Parameter (-- * Parameter
Simulation/Aivika/Parameter/Random.hs view
@@ -9,6 +9,15 @@ -- -- This module defines the random parameters of simulation experiments. --+-- To create a parameter that would return the same value within the simulation run,+-- you should memoize the computation with help of 'memoParameter', which is important+-- for the Monte-Carlo simulation.+--+-- To create a random function that would return the same values in the integration+-- time points within the simulation run, you should either lift the computation to+-- the 'Dynamics' computation and then memoize it too but using the 'memo0Dynamics'+-- function for that computation, or just take the predefined function that does+-- namely this. module Simulation.Aivika.Parameter.Random (randomUniform,@@ -16,7 +25,9 @@ randomExponential, randomErlang, randomPoisson,- randomBinomial) where+ randomBinomial,+ randomTrue,+ randomFalse) where import System.Random @@ -25,17 +36,10 @@ import Simulation.Aivika.Generator import Simulation.Aivika.Internal.Specs import Simulation.Aivika.Internal.Parameter+import Simulation.Aivika.Dynamics+import Simulation.Aivika.Dynamics.Memo.Unboxed -- | Computation that generates a new random number distributed uniformly.------ To create a parameter that would return the same value within the simulation run,--- you should memoize the computation, which is important for the Monte-Carlo simulation.------ To create a random function that would return the same values in the integration--- time points within the simulation run, you should either lift the computation to--- the @Dynamics@ computation and then memoize it too but using the corresponded--- function for that computation, or just take the predefined function that does--- namely this. randomUniform :: Double -- ^ minimum -> Double -- ^ maximum -> Parameter Double@@ -45,15 +49,6 @@ in generatorUniform g min max -- | Computation that generates a new random number distributed normally.------ To create a parameter that would return the same value within the simulation run,--- you should memoize the computation, which is important for the Monte-Carlo simulation.------ To create a random function that would return the same values in the integration--- time points within the simulation run, you should either lift the computation to--- the @Dynamics@ computation and then memoize it too but using the corresponded--- function for that computation, or just take the predefined function that does--- namely this. randomNormal :: Double -- ^ mean -> Double -- ^ deviation -> Parameter Double@@ -64,15 +59,6 @@ -- | Computation that returns a new exponential random number with the specified mean -- (the reciprocal of the rate).------ To create a parameter that would return the same value within the simulation run,--- you should memoize the computation, which is important for the Monte-Carlo simulation.------ To create a random function that would return the same values in the integration--- time points within the simulation run, you should either lift the computation to--- the @Dynamics@ computation and then memoize it too but using the corresponded--- function for that computation, or just take the predefined function that does--- namely this. randomExponential :: Double -- ^ the mean (the reciprocal of the rate) -> Parameter Double@@ -83,15 +69,6 @@ -- | Computation that returns a new Erlang random number with the specified scale -- (the reciprocal of the rate) and integer shape.------ To create a parameter that would return the same value within the simulation run,--- you should memoize the computation, which is important for the Monte-Carlo simulation.------ To create a random function that would return the same values in the integration--- time points within the simulation run, you should either lift the computation to--- the @Dynamics@ computation and then memoize it too but using the corresponded--- function for that computation, or just take the predefined function that does--- namely this. randomErlang :: Double -- ^ the scale (the reciprocal of the rate) -> Int@@ -103,15 +80,6 @@ in generatorErlang g beta m -- | Computation that returns a new Poisson random number with the specified mean.------ To create a parameter that would return the same value within the simulation run,--- you should memoize the computation, which is important for the Monte-Carlo simulation.------ To create a random function that would return the same values in the integration--- time points within the simulation run, you should either lift the computation to--- the @Dynamics@ computation and then memoize it too but using the corresponded--- function for that computation, or just take the predefined function that does--- namely this. randomPoisson :: Double -- ^ the mean -> Parameter Int@@ -122,15 +90,6 @@ -- | Computation that returns a new binomial random number with the specified -- probability and trials.------ To create a parameter that would return the same value within the simulation run,--- you should memoize the computation, which is important for the Monte-Carlo simulation.------ To create a random function that would return the same values in the integration--- time points within the simulation run, you should either lift the computation to--- the @Dynamics@ computation and then memoize it too but using the corresponded--- function for that computation, or just take the predefined function that does--- namely this. randomBinomial :: Double -- ^ the probability -> Int -- ^ the number of trials -> Parameter Int@@ -138,3 +97,17 @@ Parameter $ \r -> let g = runGenerator r in generatorBinomial g prob trials++-- | Computation that returns 'True' in case of success.+randomTrue :: Double -- ^ the probability of the success+ -> Parameter Bool+randomTrue p =+ do x <- randomUniform 0 1+ return (x <= p)++-- | Computation that returns 'False' in case of success.+randomFalse :: Double -- ^ the probability of the success+ -> Parameter Bool+randomFalse p =+ do x <- randomUniform 0 1+ return (x > p)
Simulation/Aivika/Process.hs view
@@ -35,7 +35,7 @@ -- * Spawning Processes spawnProcess, spawnProcessUsingId,- -- * Enqueuing Process+ -- * Enqueueing Process enqueueProcess, enqueueProcessUsingId, -- * Creating Process Identifier@@ -49,7 +49,7 @@ passivateProcess, processPassive, reactivateProcess,- cancelProcessUsingId,+ cancelProcessWithId, cancelProcess, processCancelled, -- * Awaiting Signal
Simulation/Aivika/Processor.hs view
@@ -17,6 +17,8 @@ statefulProcessor, -- * Specifying Identifier processorUsingId,+ -- * Prefetch Processor+ prefetchProcessor, -- * Buffer Processor bufferProcessor, bufferProcessorLoop,@@ -291,25 +293,29 @@ runStream output -- | Like 'bufferProcessor' but allows creating a loop when some items--- can be returned for processing them again. It is very useful for--- modeling the processors with queues and loop-backs.+-- can be processed repeatedly. It is very useful for modeling the processors +-- with queues and loop-backs. bufferProcessorLoop :: (Stream a -> Stream c -> Process ()) -- ^ consume two streams: the input values of type @a@ -- and the values of type @c@ returned by the loop -> Stream d -- ^ the stream of data that may become results- -> Processor d (Either c b)- -- ^ process and then decide what values of type @c@- -- should be processed again+ -> Processor d (Either e b)+ -- ^ process and then decide what values of type @e@+ -- should be processed in the loop (this is a condition)+ -> Processor e c+ -- ^ process in the loop and then return a value+ -- of type @c@ to the input again (this is a loop body) -> Processor a b-bufferProcessorLoop consume preoutput filter =+bufferProcessorLoop consume preoutput cond body = Processor $ \xs -> Cons $ do (reverted, output) <- liftSimulation $ partitionEitherStream $- runProcessor filter preoutput- spawnProcess CancelTogether (consume xs reverted)+ runProcessor cond preoutput+ spawnProcess CancelTogether + (consume xs $ runProcessor body reverted) runStream output -- | Return a processor with help of which we can model the queue.@@ -335,7 +341,7 @@ queueProcessor :: (a -> Process ()) -- ^ enqueue the input item and wait -- while the queue is full if required- -- so that there was no hanging items+ -- so that there were no hanging items -> Process b -- ^ dequeue an output item -> Processor a b@@ -345,21 +351,24 @@ (consumeStream enqueue) (repeatProcess dequeue) --- | Like 'queueProcessor' creates a queue processor but allows creating--- a loop when some items can be returned and added to the queue again.--- Also it allows specifying how two input streams of data can be merged.+-- | Like 'queueProcessor' creates a queue processor but with a loop when some items +-- can be processed and then added to the queue again. Also it allows specifying +-- how two input streams of data can be merged. queueProcessorLoopMerging :: (Stream a -> Stream d -> Stream e) -- ^ merge two streams: the input values of type @a@ -- and the values of type @d@ returned by the loop -> (e -> Process ()) -- ^ enqueue the input item and wait -- while the queue is full if required- -- so that there was no hanging items+ -- so that there were no hanging items -> Process c -- ^ dequeue an item for the further processing- -> Processor c (Either d b)- -- ^ process and then decide what values of type @d@- -- should be processed again+ -> Processor c (Either f b)+ -- ^ process and then decide what values of type @f@+ -- should be processed in the loop (this is a condition)+ -> Processor f d+ -- ^ process in the loop and then return a value+ -- of type @d@ to the queue again (this is a loop body) -> Processor a b -- ^ the buffering processor queueProcessorLoopMerging merge enqueue dequeue =@@ -369,39 +378,44 @@ merge bs cs) (repeatProcess dequeue) --- | Like 'queueProcessorLoopMerging' creates a queue processor and allows--- creating a loop when some items can be returned and added to the queue again.--- Only it sequentially merges two input streams of data: one stream--- that come from the external source and another stream of data returned--- by the loop. The first stream has a priority over the second one.+-- | Like 'queueProcessorLoopMerging' creates a queue processor with a loop when+-- some items can be processed and then added to the queue again. Only it sequentially +-- merges two input streams of data: one stream that come from the external source and +-- another stream of data returned by the loop. The first stream has a priority over +-- the second one. queueProcessorLoopSeq :: (a -> Process ()) -- ^ enqueue the input item and wait -- while the queue is full if required- -- so that there was no hanging items+ -- so that there were no hanging items -> Process c -- ^ dequeue an item for the further processing- -> Processor c (Either a b)- -- ^ process and then decide what values of type @a@- -- should be processed again+ -> Processor c (Either e b)+ -- ^ process and then decide what values of type @e@+ -- should be processed in the loop (this is a condition)+ -> Processor e a+ -- ^ process in the loop and then return a value+ -- of type @a@ to the queue again (this is a loop body) -> Processor a b -- ^ the buffering processor queueProcessorLoopSeq = queueProcessorLoopMerging mergeStreams --- | Like 'queueProcessorLoopMerging' creates a queue processor and allows--- creating a loop when some items can be returned and added to the queue again.--- Only it runs two simultaneous processes to enqueue the input streams of data:--- one stream that come from the external source and another stream of data returned--- by the loop.+-- | Like 'queueProcessorLoopMerging' creates a queue processor with a loop when+-- some items can be processed and then added to the queue again. Only it runs two +-- simultaneous processes to enqueue the input streams of data: one stream that come +-- from the external source and another stream of data returned by the loop. queueProcessorLoopParallel :: (a -> Process ()) -- ^ enqueue the input item and wait -- while the queue is full if required- -- so that there was no hanging items+ -- so that there were no hanging items -> Process c -- ^ dequeue an item for the further processing- -> Processor c (Either a b)- -- ^ process and then decide what values of type @a@- -- should be processed again+ -> Processor c (Either e b)+ -- ^ process and then decide what values of type @e@+ -- should be processed in the loop (this is a condition)+ -> Processor e a+ -- ^ process in the loop and then return a value+ -- of type @a@ to the queue again (this is a loop body) -> Processor a b -- ^ the buffering processor queueProcessorLoopParallel enqueue dequeue =@@ -412,3 +426,13 @@ spawnProcess CancelTogether $ consumeStream enqueue cs) (repeatProcess dequeue)++-- | This is a prefetch processor that requests for one more data item from +-- the input in advance while the latest item is not yet fully processed in +-- the chain of streams, usually by other processors.+--+-- You can think of this as the prefetched processor could place its latest +-- data item in some temporary space for later use, which is very useful +-- for modeling a sequence of separate and independent work places.+prefetchProcessor :: Processor a a+prefetchProcessor = Processor prefetchStream
Simulation/Aivika/Queue.hs view
@@ -26,27 +26,27 @@ newPriorityQueue, newQueue, -- * Queue Properties and Activities- queueInputStrategy,- queueStoringStrategy,- queueOutputStrategy,+ enqueueStrategy,+ enqueueStoringStrategy,+ dequeueStrategy, queueNull, queueFull, queueMaxCount, queueCount,- queueLostCount,- queueInputCount,- queueStoreCount,- queueOutputRequestCount,- queueOutputCount,+ enqueueCount,+ enqueueLostCount,+ enqueueStoreCount,+ dequeueCount,+ dequeueExtractCount, queueLoadFactor,- queueInputRate,- queueStoreRate,- queueOutputRequestRate,- queueOutputRate,+ enqueueRate,+ enqueueStoreRate,+ dequeueRate,+ dequeueExtractRate, queueWaitTime, queueTotalWaitTime,- queueInputWaitTime,- queueOutputWaitTime,+ enqueueWaitTime,+ dequeueWaitTime, -- * Dequeuing and Enqueuing dequeue, dequeueWithOutputPriority,@@ -72,26 +72,26 @@ queueFullChanged_, queueCountChanged, queueCountChanged_,- queueLostCountChanged,- queueLostCountChanged_,- queueInputCountChanged,- queueInputCountChanged_,- queueStoreCountChanged,- queueStoreCountChanged_,- queueOutputRequestCountChanged,- queueOutputRequestCountChanged_,- queueOutputCountChanged,- queueOutputCountChanged_,+ enqueueCountChanged,+ enqueueCountChanged_,+ enqueueLostCountChanged,+ enqueueLostCountChanged_,+ enqueueStoreCountChanged,+ enqueueStoreCountChanged_,+ dequeueCountChanged,+ dequeueCountChanged_,+ dequeueExtractCountChanged,+ dequeueExtractCountChanged_, queueLoadFactorChanged, queueLoadFactorChanged_, queueWaitTimeChanged, queueWaitTimeChanged_, queueTotalWaitTimeChanged, queueTotalWaitTimeChanged_,- queueInputWaitTimeChanged,- queueInputWaitTimeChanged_,- queueOutputWaitTimeChanged,- queueOutputWaitTimeChanged_,+ enqueueWaitTimeChanged,+ enqueueWaitTimeChanged_,+ dequeueWaitTimeChanged,+ dequeueWaitTimeChanged_, -- * Basic Signals enqueueInitiated, enqueueStored,@@ -143,33 +143,33 @@ type PriorityQueue a = Queue FCFS DLL.DoubleLinkedList StaticPriorities PQ.PriorityQueue FCFS DLL.DoubleLinkedList a --- | Represents the queue using the specified strategies for input @si@,--- internal storing (in memory) @sm@ and output @so@, where @a@ denotes+-- | Represents a queue using the specified strategies for enqueueing (input), @si@,+-- internal storing (in memory), @sm@, and dequeueing (output), @so@, where @a@ denotes -- the type of items stored in the queue. Types @qi@, @qm@ and @qo@ are -- determined automatically and you should not care about them - they -- are dependent types. data Queue si qi sm qm so qo a = Queue { queueMaxCount :: Int, -- ^ The queue capacity.- queueInputStrategy :: si,- -- ^ The strategy applied to the input (enqueuing) process.- queueStoringStrategy :: sm,+ enqueueStrategy :: si,+ -- ^ The strategy applied to the enqueueing (input) processes when the queue is full.+ enqueueStoringStrategy :: sm, -- ^ The strategy applied when storing (in memory) items in the queue.- queueOutputStrategy :: so,- -- ^ The strategy applied to the output (dequeuing) process.- queueInputRes :: Resource si qi,+ dequeueStrategy :: so,+ -- ^ The strategy applied to the dequeueing (output) processes when the queue is empty.+ enqueueRes :: Resource si qi, queueStore :: qm (QueueItem a),- queueOutputRes :: Resource so qo,+ dequeueRes :: Resource so qo, queueCountRef :: IORef Int,- queueLostCountRef :: IORef Int,- queueInputCountRef :: IORef Int,- queueStoreCountRef :: IORef Int,- queueOutputRequestCountRef :: IORef Int,- queueOutputCountRef :: IORef Int,+ enqueueCountRef :: IORef Int,+ enqueueLostCountRef :: IORef Int,+ enqueueStoreCountRef :: IORef Int,+ dequeueCountRef :: IORef Int,+ dequeueExtractCountRef :: IORef Int, queueWaitTimeRef :: IORef (SamplingStats Double), queueTotalWaitTimeRef :: IORef (SamplingStats Double),- queueInputWaitTimeRef :: IORef (SamplingStats Double),- queueOutputWaitTimeRef :: IORef (SamplingStats Double),+ enqueueWaitTimeRef :: IORef (SamplingStats Double),+ dequeueWaitTimeRef :: IORef (SamplingStats Double), enqueueInitiatedSource :: SignalSource a, enqueueLostSource :: SignalSource a, enqueueStoredSource :: SignalSource a,@@ -209,18 +209,18 @@ QueueStrategy sm qm, QueueStrategy so qo) => si- -- ^ the strategy applied to the input (enqueuing) process+ -- ^ the strategy applied to the enqueueing (input) processes when the queue is full -> sm -- ^ the strategy applied when storing items in the queue -> so- -- ^ the strategy applied to the output (dequeuing) process+ -- ^ the strategy applied to the dequeueing (output) processes when the queue is empty -> Int -- ^ the queue capacity -> Simulation (Queue si qi sm qm so qo a) newQueue si sm so count = do i <- liftIO $ newIORef 0- l <- liftIO $ newIORef 0 ci <- liftIO $ newIORef 0+ cl <- liftIO $ newIORef 0 cm <- liftIO $ newIORef 0 cr <- liftIO $ newIORef 0 co <- liftIO $ newIORef 0@@ -237,22 +237,22 @@ s4 <- newSignalSource s5 <- newSignalSource return Queue { queueMaxCount = count,- queueInputStrategy = si,- queueStoringStrategy = sm,- queueOutputStrategy = so,- queueInputRes = ri,+ enqueueStrategy = si,+ enqueueStoringStrategy = sm,+ dequeueStrategy = so,+ enqueueRes = ri, queueStore = qm,- queueOutputRes = ro,+ dequeueRes = ro, queueCountRef = i,- queueLostCountRef = l,- queueInputCountRef = ci,- queueStoreCountRef = cm,- queueOutputRequestCountRef = cr,- queueOutputCountRef = co,+ enqueueCountRef = ci,+ enqueueLostCountRef = cl,+ enqueueStoreCountRef = cm,+ dequeueCountRef = cr,+ dequeueExtractCountRef = co, queueWaitTimeRef = w, queueTotalWaitTimeRef = wt,- queueInputWaitTimeRef = wi,- queueOutputWaitTimeRef = wo,+ enqueueWaitTimeRef = wi,+ dequeueWaitTimeRef = wo, enqueueInitiatedSource = s1, enqueueLostSource = s2, enqueueStoredSource = s3,@@ -312,92 +312,92 @@ queueCountChanged_ q = mapSignal (const ()) (enqueueStored q) <> mapSignal (const ()) (dequeueExtracted q)- --- | Return the number of lost items.------ See also 'queueLostCountChanged' and 'queueLostCountChanged_'.-queueLostCount :: Queue si qi sm qm so qo a -> Event Int-queueLostCount q =- Event $ \p -> readIORef (queueLostCountRef q)- --- | Signal when the 'queueLostCount' property value has changed.-queueLostCountChanged :: Queue si qi sm qm so qo a -> Signal Int-queueLostCountChanged q =- mapSignalM (const $ queueLostCount q) (queueLostCountChanged_ q)- --- | Signal when the 'queueLostCount' property value has changed.-queueLostCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()-queueLostCountChanged_ q =- mapSignal (const ()) (enqueueLost q) -- | Return the total number of input items that were enqueued. ----- See also 'queueInputCountChanged' and 'queueInputCountChanged_'.-queueInputCount :: Queue si qi sm qm so qo a -> Event Int-queueInputCount q =- Event $ \p -> readIORef (queueInputCountRef q)+-- See also 'enqueueCountChanged' and 'enqueueCountChanged_'.+enqueueCount :: Queue si qi sm qm so qo a -> Event Int+enqueueCount q =+ Event $ \p -> readIORef (enqueueCountRef q) --- | Signal when the 'queueInputCount' property value has changed.-queueInputCountChanged :: Queue si qi sm qm so qo a -> Signal Int-queueInputCountChanged q =- mapSignalM (const $ queueInputCount q) (queueInputCountChanged_ q)+-- | Signal when the 'enqueueCount' property value has changed.+enqueueCountChanged :: Queue si qi sm qm so qo a -> Signal Int+enqueueCountChanged q =+ mapSignalM (const $ enqueueCount q) (enqueueCountChanged_ q) --- | Signal when the 'queueInputCount' property value has changed.-queueInputCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()-queueInputCountChanged_ q =+-- | Signal when the 'enqueueCount' property value has changed.+enqueueCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()+enqueueCountChanged_ q = mapSignal (const ()) (enqueueInitiated q)+ +-- | Return the number of lost items.+--+-- See also 'enqueueLostCountChanged' and 'enqueueLostCountChanged_'.+enqueueLostCount :: Queue si qi sm qm so qo a -> Event Int+enqueueLostCount q =+ Event $ \p -> readIORef (enqueueLostCountRef q)+ +-- | Signal when the 'enqueueLostCount' property value has changed.+enqueueLostCountChanged :: Queue si qi sm qm so qo a -> Signal Int+enqueueLostCountChanged q =+ mapSignalM (const $ enqueueLostCount q) (enqueueLostCountChanged_ q)+ +-- | Signal when the 'enqueueLostCount' property value has changed.+enqueueLostCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()+enqueueLostCountChanged_ q =+ mapSignal (const ()) (enqueueLost q) -- | Return the total number of input items that were stored. ----- See also 'queueStoreCountChanged' and 'queueStoreCountChanged_'.-queueStoreCount :: Queue si qi sm qm so qo a -> Event Int-queueStoreCount q =- Event $ \p -> readIORef (queueStoreCountRef q)+-- See also 'enqueueStoreCountChanged' and 'enqueueStoreCountChanged_'.+enqueueStoreCount :: Queue si qi sm qm so qo a -> Event Int+enqueueStoreCount q =+ Event $ \p -> readIORef (enqueueStoreCountRef q) --- | Signal when the 'queueStoreCount' property value has changed.-queueStoreCountChanged :: Queue si qi sm qm so qo a -> Signal Int-queueStoreCountChanged q =- mapSignalM (const $ queueStoreCount q) (queueStoreCountChanged_ q)+-- | Signal when the 'enqueueStoreCount' property value has changed.+enqueueStoreCountChanged :: Queue si qi sm qm so qo a -> Signal Int+enqueueStoreCountChanged q =+ mapSignalM (const $ enqueueStoreCount q) (enqueueStoreCountChanged_ q) --- | Signal when the 'queueStoreCount' property value has changed.-queueStoreCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()-queueStoreCountChanged_ q =+-- | Signal when the 'enqueueStoreCount' property value has changed.+enqueueStoreCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()+enqueueStoreCountChanged_ q = mapSignal (const ()) (enqueueStored q) -- | Return the total number of requests for dequeueing the items,--- not taking into account the attempts to dequeue immediately+-- not taking into account the failed attempts to dequeue immediately -- without suspension. ----- See also 'queueOutputRequestCountChanged' and 'queueOutputRequestCountChanged_'.-queueOutputRequestCount :: Queue si qi sm qm so qo a -> Event Int-queueOutputRequestCount q =- Event $ \p -> readIORef (queueOutputRequestCountRef q)+-- See also 'dequeueCountChanged' and 'dequeueCountChanged_'.+dequeueCount :: Queue si qi sm qm so qo a -> Event Int+dequeueCount q =+ Event $ \p -> readIORef (dequeueCountRef q) --- | Signal when the 'queueOutputRequestCount' property value has changed.-queueOutputRequestCountChanged :: Queue si qi sm qm so qo a -> Signal Int-queueOutputRequestCountChanged q =- mapSignalM (const $ queueOutputRequestCount q) (queueOutputRequestCountChanged_ q)+-- | Signal when the 'dequeueCount' property value has changed.+dequeueCountChanged :: Queue si qi sm qm so qo a -> Signal Int+dequeueCountChanged q =+ mapSignalM (const $ dequeueCount q) (dequeueCountChanged_ q) --- | Signal when the 'queueOutputRequestCount' property value has changed.-queueOutputRequestCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()-queueOutputRequestCountChanged_ q =+-- | Signal when the 'dequeueCount' property value has changed.+dequeueCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()+dequeueCountChanged_ q = mapSignal (const ()) (dequeueRequested q) --- | Return the total number of output items that were dequeued.+-- | Return the total number of output items that were actually dequeued. ----- See also 'queueOutputCountChanged' and 'queueOutputCountChanged_'.-queueOutputCount :: Queue si qi sm qm so qo a -> Event Int-queueOutputCount q =- Event $ \p -> readIORef (queueOutputCountRef q)+-- See also 'dequeueExtractCountChanged' and 'dequeueExtractCountChanged_'.+dequeueExtractCount :: Queue si qi sm qm so qo a -> Event Int+dequeueExtractCount q =+ Event $ \p -> readIORef (dequeueExtractCountRef q) --- | Signal when the 'queueOutputCount' property value has changed.-queueOutputCountChanged :: Queue si qi sm qm so qo a -> Signal Int-queueOutputCountChanged q =- mapSignalM (const $ queueOutputCount q) (queueOutputCountChanged_ q)+-- | Signal when the 'dequeueExtractCount' property value has changed.+dequeueExtractCountChanged :: Queue si qi sm qm so qo a -> Signal Int+dequeueExtractCountChanged q =+ mapSignalM (const $ dequeueExtractCount q) (dequeueExtractCountChanged_ q) --- | Signal when the 'queueOutputCount' property value has changed.-queueOutputCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()-queueOutputCountChanged_ q =+-- | Signal when the 'dequeueExtractCount' property value has changed.+dequeueExtractCountChanged_ :: Queue si qi sm qm so qo a -> Signal ()+dequeueExtractCountChanged_ q = mapSignal (const ()) (dequeueExtracted q) -- | Return the load factor: the queue size divided by its maximum size.@@ -423,41 +423,41 @@ -- | Return the rate of the input items that were enqueued: how many items -- per time.-queueInputRate :: Queue si qi sm qm so qo a -> Event Double-queueInputRate q =+enqueueRate :: Queue si qi sm qm so qo a -> Event Double+enqueueRate q = Event $ \p ->- do x <- readIORef (queueInputCountRef q)+ do x <- readIORef (enqueueCountRef q) let t0 = spcStartTime $ pointSpecs p t = pointTime p return (fromIntegral x / (t - t0)) -- | Return the rate of the items that were stored: how many items -- per time.-queueStoreRate :: Queue si qi sm qm so qo a -> Event Double-queueStoreRate q =+enqueueStoreRate :: Queue si qi sm qm so qo a -> Event Double+enqueueStoreRate q = Event $ \p ->- do x <- readIORef (queueStoreCountRef q)+ do x <- readIORef (enqueueStoreCountRef q) let t0 = spcStartTime $ pointSpecs p t = pointTime p return (fromIntegral x / (t - t0)) -- | Return the rate of the requests for dequeueing the items: how many requests--- per time. It does not include the attempts to dequeue immediately+-- per time. It does not include the failed attempts to dequeue immediately -- without suspension.-queueOutputRequestRate :: Queue si qi sm qm so qo a -> Event Double-queueOutputRequestRate q =+dequeueRate :: Queue si qi sm qm so qo a -> Event Double+dequeueRate q = Event $ \p ->- do x <- readIORef (queueOutputRequestCountRef q)+ do x <- readIORef (dequeueCountRef q) let t0 = spcStartTime $ pointSpecs p t = pointTime p return (fromIntegral x / (t - t0)) --- | Return the rate of the output items that were dequeued: how many items+-- | Return the rate of the output items that were actually dequeued: how many items -- per time.-queueOutputRate :: Queue si qi sm qm so qo a -> Event Double-queueOutputRate q =+dequeueExtractRate :: Queue si qi sm qm so qo a -> Event Double+dequeueExtractRate q = Event $ \p ->- do x <- readIORef (queueOutputCountRef q)+ do x <- readIORef (dequeueExtractCountRef q) let t0 = spcStartTime $ pointSpecs p t = pointTime p return (fromIntegral x / (t - t0))@@ -500,40 +500,40 @@ queueTotalWaitTimeChanged_ q = mapSignal (const ()) (dequeueExtracted q) --- | Return the input wait time from the time at which the enqueueing operation+-- | Return the enqueue wait time from the time at which the enqueueing operation -- was initiated to the time at which the item was stored in the queue. ----- See also 'queueInputWaitTimeChanged' and 'queueInputWaitTimeChanged_'.-queueInputWaitTime :: Queue si qi sm qm so qo a -> Event (SamplingStats Double)-queueInputWaitTime q =- Event $ \p -> readIORef (queueInputWaitTimeRef q)+-- See also 'enqueueWaitTimeChanged' and 'enqueueWaitTimeChanged_'.+enqueueWaitTime :: Queue si qi sm qm so qo a -> Event (SamplingStats Double)+enqueueWaitTime q =+ Event $ \p -> readIORef (enqueueWaitTimeRef q) --- | Signal when the 'queueInputWaitTime' property value has changed.-queueInputWaitTimeChanged :: Queue si qi sm qm so qo a -> Signal (SamplingStats Double)-queueInputWaitTimeChanged q =- mapSignalM (const $ queueInputWaitTime q) (queueInputWaitTimeChanged_ q)+-- | Signal when the 'enqueueWaitTime' property value has changed.+enqueueWaitTimeChanged :: Queue si qi sm qm so qo a -> Signal (SamplingStats Double)+enqueueWaitTimeChanged q =+ mapSignalM (const $ enqueueWaitTime q) (enqueueWaitTimeChanged_ q) --- | Signal when the 'queueInputWaitTime' property value has changed.-queueInputWaitTimeChanged_ :: Queue si qi sm qm so qo a -> Signal ()-queueInputWaitTimeChanged_ q =+-- | Signal when the 'enqueueWaitTime' property value has changed.+enqueueWaitTimeChanged_ :: Queue si qi sm qm so qo a -> Signal ()+enqueueWaitTimeChanged_ q = mapSignal (const ()) (enqueueStored q) --- | Return the output wait time from the time at which the item was requested+-- | Return the dequeue wait time from the time at which the item was requested -- for dequeueing to the time at which it was actually dequeued. ----- See also 'queueOutputWaitTimeChanged' and 'queueOutputWaitTimeChanged_'.-queueOutputWaitTime :: Queue si qi sm qm so qo a -> Event (SamplingStats Double)-queueOutputWaitTime q =- Event $ \p -> readIORef (queueOutputWaitTimeRef q)+-- See also 'dequeueWaitTimeChanged' and 'dequeueWaitTimeChanged_'.+dequeueWaitTime :: Queue si qi sm qm so qo a -> Event (SamplingStats Double)+dequeueWaitTime q =+ Event $ \p -> readIORef (dequeueWaitTimeRef q) --- | Signal when the 'queueOutputWaitTime' property value has changed.-queueOutputWaitTimeChanged :: Queue si qi sm qm so qo a -> Signal (SamplingStats Double)-queueOutputWaitTimeChanged q =- mapSignalM (const $ queueOutputWaitTime q) (queueOutputWaitTimeChanged_ q)+-- | Signal when the 'dequeueWaitTime' property value has changed.+dequeueWaitTimeChanged :: Queue si qi sm qm so qo a -> Signal (SamplingStats Double)+dequeueWaitTimeChanged q =+ mapSignalM (const $ dequeueWaitTime q) (dequeueWaitTimeChanged_ q) --- | Signal when the 'queueOutputWaitTime' property value has changed.-queueOutputWaitTimeChanged_ :: Queue si qi sm qm so qo a -> Signal ()-queueOutputWaitTimeChanged_ q =+-- | Signal when the 'dequeueWaitTime' property value has changed.+dequeueWaitTimeChanged_ :: Queue si qi sm qm so qo a -> Signal ()+dequeueWaitTimeChanged_ q = mapSignal (const ()) (dequeueExtracted q) -- | Dequeue suspending the process if the queue is empty.@@ -546,7 +546,7 @@ -- ^ the dequeued value dequeue q = do t <- liftEvent $ dequeueRequest q- requestResource (queueOutputRes q)+ requestResource (dequeueRes q) liftEvent $ dequeueExtract q t -- | Dequeue with the output priority suspending the process if the queue is empty.@@ -561,7 +561,7 @@ -- ^ the dequeued value dequeueWithOutputPriority q po = do t <- liftEvent $ dequeueRequest q- requestResourceWithPriority (queueOutputRes q) po+ requestResourceWithPriority (dequeueRes q) po liftEvent $ dequeueExtract q t -- | Try to dequeue immediately.@@ -572,7 +572,7 @@ -> Event (Maybe a) -- ^ the dequeued value of 'Nothing' tryDequeue q =- do x <- tryRequestResourceWithinEvent (queueOutputRes q)+ do x <- tryRequestResourceWithinEvent (dequeueRes q) if x then do t <- dequeueRequest q fmap Just $ dequeueExtract q t@@ -589,7 +589,7 @@ -> Process () enqueue q a = do i <- liftEvent $ enqueueInitiate q a- requestResource (queueInputRes q)+ requestResource (enqueueRes q) liftEvent $ enqueueStore q i -- | Enqueue with the input priority the item suspending the process if the queue is full. @@ -605,7 +605,7 @@ -> Process () enqueueWithInputPriority q pi a = do i <- liftEvent $ enqueueInitiate q a- requestResourceWithPriority (queueInputRes q) pi+ requestResourceWithPriority (enqueueRes q) pi liftEvent $ enqueueStore q i -- | Enqueue with the storing priority the item suspending the process if the queue is full. @@ -621,7 +621,7 @@ -> Process () enqueueWithStoringPriority q pm a = do i <- liftEvent $ enqueueInitiate q a- requestResource (queueInputRes q)+ requestResource (enqueueRes q) liftEvent $ enqueueStoreWithPriority q pm i -- | Enqueue with the input and storing priorities the item suspending the process if the queue is full. @@ -639,7 +639,7 @@ -> Process () enqueueWithInputStoringPriorities q pi pm a = do i <- liftEvent $ enqueueInitiate q a- requestResourceWithPriority (queueInputRes q) pi+ requestResourceWithPriority (enqueueRes q) pi liftEvent $ enqueueStoreWithPriority q pm i -- | Try to enqueue the item. Return 'False' in the monad if the queue is full.@@ -651,7 +651,7 @@ -- ^ the item which we try to enqueue -> Event Bool tryEnqueue q a =- do x <- tryRequestResourceWithinEvent (queueInputRes q)+ do x <- tryRequestResourceWithinEvent (enqueueRes q) if x then do enqueueInitiate q a >>= enqueueStore q return True@@ -669,7 +669,7 @@ -- ^ the item which we try to enqueue -> Event Bool tryEnqueueWithStoringPriority q pm a =- do x <- tryRequestResourceWithinEvent (queueInputRes q)+ do x <- tryRequestResourceWithinEvent (enqueueRes q) if x then do enqueueInitiate q a >>= enqueueStoreWithPriority q pm return True@@ -685,7 +685,7 @@ -- ^ the item which we try to enqueue -> Event Bool enqueueOrLost q a =- do x <- tryRequestResourceWithinEvent (queueInputRes q)+ do x <- tryRequestResourceWithinEvent (enqueueRes q) if x then do enqueueInitiate q a >>= enqueueStore q return True@@ -704,7 +704,7 @@ -- ^ the item which we try to enqueue -> Event Bool enqueueWithStoringPriorityOrLost q pm a =- do x <- tryRequestResourceWithinEvent (queueInputRes q)+ do x <- tryRequestResourceWithinEvent (enqueueRes q) if x then do enqueueInitiate q a >>= enqueueStoreWithPriority q pm return True@@ -779,7 +779,7 @@ enqueueInitiate q a = Event $ \p -> do let t = pointTime p- modifyIORef (queueInputCountRef q) (+ 1)+ modifyIORef' (enqueueCountRef q) (+ 1) invokeEvent p $ triggerSignal (enqueueInitiatedSource q) a return QueueItem { itemValue = a,@@ -799,13 +799,13 @@ Event $ \p -> do let i' = i { itemStoringTime = pointTime p } -- now we have the actual time of storing invokeEvent p $- strategyEnqueue (queueStoringStrategy q) (queueStore q) i'- modifyIORef (queueCountRef q) (+ 1)- modifyIORef (queueStoreCountRef q) (+ 1)+ strategyEnqueue (enqueueStoringStrategy q) (queueStore q) i'+ modifyIORef' (queueCountRef q) (+ 1)+ modifyIORef' (enqueueStoreCountRef q) (+ 1) invokeEvent p $ enqueueStat q i' invokeEvent p $- releaseResourceWithinEvent (queueOutputRes q)+ releaseResourceWithinEvent (dequeueRes q) invokeEvent p $ triggerSignal (enqueueStoredSource q) (itemValue i') @@ -823,13 +823,13 @@ Event $ \p -> do let i' = i { itemStoringTime = pointTime p } -- now we have the actual time of storing invokeEvent p $- strategyEnqueueWithPriority (queueStoringStrategy q) (queueStore q) pm i'- modifyIORef (queueCountRef q) (+ 1)- modifyIORef (queueStoreCountRef q) (+ 1)+ strategyEnqueueWithPriority (enqueueStoringStrategy q) (queueStore q) pm i'+ modifyIORef' (queueCountRef q) (+ 1)+ modifyIORef' (enqueueStoreCountRef q) (+ 1) invokeEvent p $ enqueueStat q i' invokeEvent p $- releaseResourceWithinEvent (queueOutputRes q)+ releaseResourceWithinEvent (dequeueRes q) invokeEvent p $ triggerSignal (enqueueStoredSource q) (itemValue i') @@ -841,7 +841,7 @@ -> Event () enqueueDeny q a = Event $ \p ->- do modifyIORef (queueLostCountRef q) $ (+) 1+ do modifyIORef' (enqueueLostCountRef q) $ (+) 1 invokeEvent p $ triggerSignal (enqueueLostSource q) a @@ -856,7 +856,7 @@ Event $ \p -> do let t0 = itemInputTime i t1 = itemStoringTime i- modifyIORef (queueInputWaitTimeRef q) $+ modifyIORef' (enqueueWaitTimeRef q) $ addSamplingStats (t1 - t0) -- | Accept the dequeuing request and return the current simulation time.@@ -866,7 +866,7 @@ -- ^ the current time dequeueRequest q = Event $ \p ->- do modifyIORef (queueOutputRequestCountRef q) (+ 1)+ do modifyIORef' (dequeueCountRef q) (+ 1) invokeEvent p $ triggerSignal (dequeueRequestedSource q) () return $ pointTime p @@ -883,13 +883,13 @@ dequeueExtract q t' = Event $ \p -> do i <- invokeEvent p $- strategyDequeue (queueStoringStrategy q) (queueStore q)- modifyIORef (queueCountRef q) (+ (- 1))- modifyIORef (queueOutputCountRef q) (+ 1)+ strategyDequeue (enqueueStoringStrategy q) (queueStore q)+ modifyIORef' (queueCountRef q) (+ (- 1))+ modifyIORef' (dequeueExtractCountRef q) (+ 1) invokeEvent p $ dequeueStat q t' i invokeEvent p $- releaseResourceWithinEvent (queueInputRes q)+ releaseResourceWithinEvent (enqueueRes q) invokeEvent p $ triggerSignal (dequeueExtractedSource q) (itemValue i) return $ itemValue i@@ -909,11 +909,11 @@ do let t0 = itemInputTime i t1 = itemStoringTime i t = pointTime p- modifyIORef (queueOutputWaitTimeRef q) $+ modifyIORef' (dequeueWaitTimeRef q) $ addSamplingStats (t - t')- modifyIORef (queueTotalWaitTimeRef q) $+ modifyIORef' (queueTotalWaitTimeRef q) $ addSamplingStats (t - t0)- modifyIORef (queueWaitTimeRef q) $+ modifyIORef' (queueWaitTimeRef q) $ addSamplingStats (t - t1) -- | Wait while the queue is full.@@ -942,31 +942,31 @@ -- properties and activities using the specified indent. queueSummary :: (Show si, Show sm, Show so) => Queue si qi sm qm so qo a -> Int -> Event ShowS queueSummary q indent =- do let si = queueInputStrategy q- sm = queueStoringStrategy q- so = queueOutputStrategy q+ do let si = enqueueStrategy q+ sm = enqueueStoringStrategy q+ so = dequeueStrategy q null <- queueNull q full <- queueFull q let maxCount = queueMaxCount q count <- queueCount q- lostCount <- queueLostCount q- inputCount <- queueInputCount q- storeCount <- queueStoreCount q- outputRequestCount <- queueOutputRequestCount q- outputCount <- queueOutputCount q+ enqueueCount <- enqueueCount q+ enqueueLostCount <- enqueueLostCount q+ enqueueStoreCount <- enqueueStoreCount q+ dequeueCount <- dequeueCount q+ dequeueExtractCount <- dequeueExtractCount q loadFactor <- queueLoadFactor q- inputRate <- queueInputRate q- storeRate <- queueStoreRate q- outputRequestRate <- queueOutputRequestRate q- outputRate <- queueOutputRate q+ enqueueRate <- enqueueRate q+ enqueueStoreRate <- enqueueStoreRate q+ dequeueRate <- dequeueRate q+ dequeueExtractRate <- dequeueExtractRate q waitTime <- queueWaitTime q totalWaitTime <- queueTotalWaitTime q- inputWaitTime <- queueInputWaitTime q- outputWaitTime <- queueOutputWaitTime q+ enqueueWaitTime <- enqueueWaitTime q+ dequeueWaitTime <- dequeueWaitTime q let tab = replicate indent ' ' return $ showString tab .- showString "the input (enqueueing) strategy = " .+ showString "the enqueueing (input) strategy = " . shows si . showString "\n" . showString tab .@@ -974,7 +974,7 @@ shows sm . showString "\n" . showString tab .- showString "the output (dequeueing) strategy = " .+ showString "the dequeueing (output) strategy = " . shows so . showString "\n" . showString tab .@@ -994,44 +994,44 @@ shows count . showString "\n" . showString tab .- showString "the lost count (number of the lost items) = " .- shows lostCount .+ showString "the enqueue count (number of the input items that were enqueued) = " .+ shows enqueueCount . showString "\n" . showString tab .- showString "the input count (number of the input items that were enqueued) = " .- shows inputCount .+ showString "the enqueue lost count (number of the lost items) = " .+ shows enqueueLostCount . showString "\n" . showString tab .- showString "the store count (number of the input items that were stored) = " .- shows storeCount .+ showString "the enqueue store count (number of the input items that were stored) = " .+ shows enqueueStoreCount . showString "\n" . showString tab .- showString "the output request count (number of requests for dequeueing an item) = " .- shows outputRequestCount .+ showString "the dequeue count (number of requests for dequeueing an item) = " .+ shows dequeueCount . showString "\n" . showString tab .- showString "the output count (number of the output items that were dequeued) = " .- shows outputCount .+ showString "the dequeue extract count (number of the output items that were dequeued) = " .+ shows dequeueExtractCount . showString "\n" . showString tab . showString "the load factor (size / max. capacity) = " . shows loadFactor . showString "\n" . showString tab .- showString "the input rate (how many input items were enqueued per time) = " .- shows inputRate .+ showString "the enqueue rate (how many input items were enqueued per time) = " .+ shows enqueueRate . showString "\n" . showString tab .- showString "the store rate (how many input items were stored per time) = " .- shows storeRate .+ showString "the enqueue store rate (how many input items were stored per time) = " .+ shows enqueueStoreRate . showString "\n" . showString tab .- showString "the output request rate (how many requests for dequeueing per time) = " .- shows outputRequestRate .+ showString "the dequeue rate (how many requests for dequeueing per time) = " .+ shows dequeueRate . showString "\n" . showString tab .- showString "the output rate (how many output items were dequeued per time) = " .- shows outputRate .+ showString "the dequeue extract rate (how many output items were dequeued per time) = " .+ shows dequeueExtractRate . showString "\n" . showString tab . showString "the wait time (when was stored -> when was dequeued) = \n\n" .@@ -1042,9 +1042,9 @@ samplingStatsSummary totalWaitTime (2 + indent) . showString "\n\n" . showString tab .- showString "the input wait time (when the enqueueing was initiated -> when was stored) = \n\n" .- samplingStatsSummary inputWaitTime (2 + indent) .+ showString "the enqueue wait time (when the enqueueing was initiated -> when was stored) = \n\n" .+ samplingStatsSummary enqueueWaitTime (2 + indent) . showString "\n\n" . showString tab .- showString "the output wait time (when was requested for dequeueing -> when was dequeued) = \n\n" .- samplingStatsSummary outputWaitTime (2 + indent)+ showString "the dequeue wait time (when was requested for dequeueing -> when was dequeued) = \n\n" .+ samplingStatsSummary dequeueWaitTime (2 + indent)
Simulation/Aivika/Queue/Infinite.hs view
@@ -23,18 +23,18 @@ newPriorityQueue, newQueue, -- * Queue Properties and Activities- queueStoringStrategy,- queueOutputStrategy,+ enqueueStoringStrategy,+ dequeueStrategy, queueNull, queueCount,- queueStoreCount,- queueOutputRequestCount,- queueOutputCount,- queueStoreRate,- queueOutputRequestRate,- queueOutputRate,+ enqueueStoreCount,+ dequeueCount,+ dequeueExtractCount,+ enqueueStoreRate,+ dequeueRate,+ dequeueExtractRate, queueWaitTime,- queueOutputWaitTime,+ dequeueWaitTime, -- * Dequeuing and Enqueuing dequeue, dequeueWithOutputPriority,@@ -48,16 +48,16 @@ queueNullChanged_, queueCountChanged, queueCountChanged_,- queueStoreCountChanged,- queueStoreCountChanged_,- queueOutputRequestCountChanged,- queueOutputRequestCountChanged_,- queueOutputCountChanged,- queueOutputCountChanged_,+ enqueueStoreCountChanged,+ enqueueStoreCountChanged_,+ dequeueCountChanged,+ dequeueCountChanged_,+ dequeueExtractCountChanged,+ dequeueExtractCountChanged_, queueWaitTimeChanged, queueWaitTimeChanged_,- queueOutputWaitTimeChanged,- queueOutputWaitTimeChanged_,+ dequeueWaitTimeChanged,+ dequeueWaitTimeChanged_, -- * Basic Signals enqueueStored, dequeueRequested,@@ -107,24 +107,24 @@ type PriorityQueue a = Queue StaticPriorities PQ.PriorityQueue FCFS DLL.DoubleLinkedList a --- | Represents the infinite queue using the specified strategies for--- internal storing (in memory) @sm@ and output @so@, where @a@ denotes+-- | Represents an infinite queue using the specified strategies for+-- internal storing (in memory), @sm@, and dequeueing (output), @so@, where @a@ denotes -- the type of items stored in the queue. Types @qm@ and @qo@ are -- determined automatically and you should not care about them - they -- are dependent types. data Queue sm qm so qo a =- Queue { queueStoringStrategy :: sm,+ Queue { enqueueStoringStrategy :: sm, -- ^ The strategy applied when storing (in memory) items in the queue.- queueOutputStrategy :: so,- -- ^ The strategy applied to the output (dequeuing) process.+ dequeueStrategy :: so,+ -- ^ The strategy applied to the dequeueing (output) processes. queueStore :: qm (QueueItem a),- queueOutputRes :: Resource so qo,+ dequeueRes :: Resource so qo, queueCountRef :: IORef Int,- queueStoreCountRef :: IORef Int,- queueOutputRequestCountRef :: IORef Int,- queueOutputCountRef :: IORef Int,+ enqueueStoreCountRef :: IORef Int,+ dequeueCountRef :: IORef Int,+ dequeueExtractCountRef :: IORef Int, queueWaitTimeRef :: IORef (SamplingStats Double),- queueOutputWaitTimeRef :: IORef (SamplingStats Double),+ dequeueWaitTimeRef :: IORef (SamplingStats Double), enqueueStoredSource :: SignalSource a, dequeueRequestedSource :: SignalSource (), dequeueExtractedSource :: SignalSource a }@@ -159,7 +159,7 @@ sm -- ^ the strategy applied when storing items in the queue -> so- -- ^ the strategy applied to the output (dequeuing) process+ -- ^ the strategy applied to the dequeueing (output) processes when the queue is empty -> Simulation (Queue sm qm so qo a) newQueue sm so = do i <- liftIO $ newIORef 0@@ -173,16 +173,16 @@ s3 <- newSignalSource s4 <- newSignalSource s5 <- newSignalSource- return Queue { queueStoringStrategy = sm,- queueOutputStrategy = so,+ return Queue { enqueueStoringStrategy = sm,+ dequeueStrategy = so, queueStore = qm,- queueOutputRes = ro,+ dequeueRes = ro, queueCountRef = i,- queueStoreCountRef = cm,- queueOutputRequestCountRef = cr,- queueOutputCountRef = co,+ enqueueStoreCountRef = cm,+ dequeueCountRef = cr,+ dequeueExtractCountRef = co, queueWaitTimeRef = w,- queueOutputWaitTimeRef = wo,+ dequeueWaitTimeRef = wo, enqueueStoredSource = s3, dequeueRequestedSource = s4, dequeueExtractedSource = s5 }@@ -225,84 +225,84 @@ -- | Return the total number of input items that were stored. ----- See also 'queueStoreCountChanged' and 'queueStoreCountChanged_'.-queueStoreCount :: Queue sm qm so qo a -> Event Int-queueStoreCount q =- Event $ \p -> readIORef (queueStoreCountRef q)+-- See also 'enqueueStoreCountChanged' and 'enqueueStoreCountChanged_'.+enqueueStoreCount :: Queue sm qm so qo a -> Event Int+enqueueStoreCount q =+ Event $ \p -> readIORef (enqueueStoreCountRef q) --- | Signal when the 'queueStoreCount' property value has changed.-queueStoreCountChanged :: Queue sm qm so qo a -> Signal Int-queueStoreCountChanged q =- mapSignalM (const $ queueStoreCount q) (queueStoreCountChanged_ q)+-- | Signal when the 'enqueueStoreCount' property value has changed.+enqueueStoreCountChanged :: Queue sm qm so qo a -> Signal Int+enqueueStoreCountChanged q =+ mapSignalM (const $ enqueueStoreCount q) (enqueueStoreCountChanged_ q) --- | Signal when the 'queueStoreCount' property value has changed.-queueStoreCountChanged_ :: Queue sm qm so qo a -> Signal ()-queueStoreCountChanged_ q =+-- | Signal when the 'enqueueStoreCount' property value has changed.+enqueueStoreCountChanged_ :: Queue sm qm so qo a -> Signal ()+enqueueStoreCountChanged_ q = mapSignal (const ()) (enqueueStored q) -- | Return the total number of requests for dequeueing the items,--- not taking into account the attempts to dequeue immediately+-- not taking into account the failed attempts to dequeue immediately -- without suspension. ----- See also 'queueOutputRequestCountChanged' and 'queueOutputRequestCountChanged_'.-queueOutputRequestCount :: Queue sm qm so qo a -> Event Int-queueOutputRequestCount q =- Event $ \p -> readIORef (queueOutputRequestCountRef q)+-- See also 'dequeueCountChanged' and 'dequeueCountChanged_'.+dequeueCount :: Queue sm qm so qo a -> Event Int+dequeueCount q =+ Event $ \p -> readIORef (dequeueCountRef q) --- | Signal when the 'queueOutputRequestCount' property value has changed.-queueOutputRequestCountChanged :: Queue sm qm so qo a -> Signal Int-queueOutputRequestCountChanged q =- mapSignalM (const $ queueOutputRequestCount q) (queueOutputRequestCountChanged_ q)+-- | Signal when the 'dequeueCount' property value has changed.+dequeueCountChanged :: Queue sm qm so qo a -> Signal Int+dequeueCountChanged q =+ mapSignalM (const $ dequeueCount q) (dequeueCountChanged_ q) --- | Signal when the 'queueOutputRequestCount' property value has changed.-queueOutputRequestCountChanged_ :: Queue sm qm so qo a -> Signal ()-queueOutputRequestCountChanged_ q =+-- | Signal when the 'dequeueCount' property value has changed.+dequeueCountChanged_ :: Queue sm qm so qo a -> Signal ()+dequeueCountChanged_ q = mapSignal (const ()) (dequeueRequested q) --- | Return the total number of output items that were dequeued.+-- | Return the total number of output items that were actually dequeued. ----- See also 'queueOutputCountChanged' and 'queueOutputCountChanged_'.-queueOutputCount :: Queue sm qm so qo a -> Event Int-queueOutputCount q =- Event $ \p -> readIORef (queueOutputCountRef q)+-- See also 'dequeueExtractCountChanged' and 'dequeueExtractCountChanged_'.+dequeueExtractCount :: Queue sm qm so qo a -> Event Int+dequeueExtractCount q =+ Event $ \p -> readIORef (dequeueExtractCountRef q) --- | Signal when the 'queueOutputCount' property value has changed.-queueOutputCountChanged :: Queue sm qm so qo a -> Signal Int-queueOutputCountChanged q =- mapSignalM (const $ queueOutputCount q) (queueOutputCountChanged_ q)+-- | Signal when the 'dequeueExtractCount' property value has changed.+dequeueExtractCountChanged :: Queue sm qm so qo a -> Signal Int+dequeueExtractCountChanged q =+ mapSignalM (const $ dequeueExtractCount q) (dequeueExtractCountChanged_ q) --- | Signal when the 'queueOutputCount' property value has changed.-queueOutputCountChanged_ :: Queue sm qm so qo a -> Signal ()-queueOutputCountChanged_ q =+-- | Signal when the 'dequeueExtractCount' property value has changed.+dequeueExtractCountChanged_ :: Queue sm qm so qo a -> Signal ()+dequeueExtractCountChanged_ q = mapSignal (const ()) (dequeueExtracted q) -- | Return the rate of the items that were stored: how many items -- per time.-queueStoreRate :: Queue sm qm so qo a -> Event Double-queueStoreRate q =+enqueueStoreRate :: Queue sm qm so qo a -> Event Double+enqueueStoreRate q = Event $ \p ->- do x <- readIORef (queueStoreCountRef q)+ do x <- readIORef (enqueueStoreCountRef q) let t0 = spcStartTime $ pointSpecs p t = pointTime p return (fromIntegral x / (t - t0)) -- | Return the rate of the requests for dequeueing the items: how many requests--- per time. It does not include the attempts to dequeue immediately+-- per time. It does not include the failed attempts to dequeue immediately -- without suspension.-queueOutputRequestRate :: Queue sm qm so qo a -> Event Double-queueOutputRequestRate q =+dequeueRate :: Queue sm qm so qo a -> Event Double+dequeueRate q = Event $ \p ->- do x <- readIORef (queueOutputRequestCountRef q)+ do x <- readIORef (dequeueCountRef q) let t0 = spcStartTime $ pointSpecs p t = pointTime p return (fromIntegral x / (t - t0)) -- | Return the rate of the output items that were dequeued: how many items -- per time.-queueOutputRate :: Queue sm qm so qo a -> Event Double-queueOutputRate q =+dequeueExtractRate :: Queue sm qm so qo a -> Event Double+dequeueExtractRate q = Event $ \p ->- do x <- readIORef (queueOutputCountRef q)+ do x <- readIORef (dequeueExtractCountRef q) let t0 = spcStartTime $ pointSpecs p t = pointTime p return (fromIntegral x / (t - t0))@@ -325,22 +325,22 @@ queueWaitTimeChanged_ q = mapSignal (const ()) (dequeueExtracted q) --- | Return the output wait time from the time at which the item was requested+-- | Return the dequeue wait time from the time at which the item was requested -- for dequeueing to the time at which it was actually dequeued. ----- See also 'queueOutputWaitTimeChanged' and 'queueOutputWaitTimeChanged_'.-queueOutputWaitTime :: Queue sm qm so qo a -> Event (SamplingStats Double)-queueOutputWaitTime q =- Event $ \p -> readIORef (queueOutputWaitTimeRef q)+-- See also 'dequeueWaitTimeChanged' and 'dequeueWaitTimeChanged_'.+dequeueWaitTime :: Queue sm qm so qo a -> Event (SamplingStats Double)+dequeueWaitTime q =+ Event $ \p -> readIORef (dequeueWaitTimeRef q) --- | Signal when the 'queueOutputWaitTime' property value has changed.-queueOutputWaitTimeChanged :: Queue sm qm so qo a -> Signal (SamplingStats Double)-queueOutputWaitTimeChanged q =- mapSignalM (const $ queueOutputWaitTime q) (queueOutputWaitTimeChanged_ q)+-- | Signal when the 'dequeueWaitTime' property value has changed.+dequeueWaitTimeChanged :: Queue sm qm so qo a -> Signal (SamplingStats Double)+dequeueWaitTimeChanged q =+ mapSignalM (const $ dequeueWaitTime q) (dequeueWaitTimeChanged_ q) --- | Signal when the 'queueOutputWaitTime' property value has changed.-queueOutputWaitTimeChanged_ :: Queue sm qm so qo a -> Signal ()-queueOutputWaitTimeChanged_ q =+-- | Signal when the 'dequeueWaitTime' property value has changed.+dequeueWaitTimeChanged_ :: Queue sm qm so qo a -> Signal ()+dequeueWaitTimeChanged_ q = mapSignal (const ()) (dequeueExtracted q) -- | Dequeue suspending the process if the queue is empty.@@ -352,7 +352,7 @@ -- ^ the dequeued value dequeue q = do t <- liftEvent $ dequeueRequest q- requestResource (queueOutputRes q)+ requestResource (dequeueRes q) liftEvent $ dequeueExtract q t -- | Dequeue with the output priority suspending the process if the queue is empty.@@ -366,7 +366,7 @@ -- ^ the dequeued value dequeueWithOutputPriority q po = do t <- liftEvent $ dequeueRequest q- requestResourceWithPriority (queueOutputRes q) po+ requestResourceWithPriority (dequeueRes q) po liftEvent $ dequeueExtract q t -- | Try to dequeue immediately.@@ -376,7 +376,7 @@ -> Event (Maybe a) -- ^ the dequeued value of 'Nothing' tryDequeue q =- do x <- tryRequestResourceWithinEvent (queueOutputRes q)+ do x <- tryRequestResourceWithinEvent (dequeueRes q) if x then do t <- dequeueRequest q fmap Just $ dequeueExtract q t@@ -431,11 +431,11 @@ do let i = QueueItem { itemValue = a, itemStoringTime = pointTime p } invokeEvent p $- strategyEnqueue (queueStoringStrategy q) (queueStore q) i- modifyIORef (queueCountRef q) (+ 1)- modifyIORef (queueStoreCountRef q) (+ 1)+ strategyEnqueue (enqueueStoringStrategy q) (queueStore q) i+ modifyIORef' (queueCountRef q) (+ 1)+ modifyIORef' (enqueueStoreCountRef q) (+ 1) invokeEvent p $- releaseResourceWithinEvent (queueOutputRes q)+ releaseResourceWithinEvent (dequeueRes q) invokeEvent p $ triggerSignal (enqueueStoredSource q) (itemValue i) @@ -454,11 +454,11 @@ do let i = QueueItem { itemValue = a, itemStoringTime = pointTime p } invokeEvent p $- strategyEnqueueWithPriority (queueStoringStrategy q) (queueStore q) pm i- modifyIORef (queueCountRef q) (+ 1)- modifyIORef (queueStoreCountRef q) (+ 1)+ strategyEnqueueWithPriority (enqueueStoringStrategy q) (queueStore q) pm i+ modifyIORef' (queueCountRef q) (+ 1)+ modifyIORef' (enqueueStoreCountRef q) (+ 1) invokeEvent p $- releaseResourceWithinEvent (queueOutputRes q)+ releaseResourceWithinEvent (dequeueRes q) invokeEvent p $ triggerSignal (enqueueStoredSource q) (itemValue i) @@ -469,7 +469,7 @@ -- ^ the current time dequeueRequest q = Event $ \p ->- do modifyIORef (queueOutputRequestCountRef q) (+ 1)+ do modifyIORef' (dequeueCountRef q) (+ 1) invokeEvent p $ triggerSignal (dequeueRequestedSource q) () return $ pointTime p @@ -485,9 +485,9 @@ dequeueExtract q t' = Event $ \p -> do i <- invokeEvent p $- strategyDequeue (queueStoringStrategy q) (queueStore q)- modifyIORef (queueCountRef q) (+ (- 1))- modifyIORef (queueOutputCountRef q) (+ 1)+ strategyDequeue (enqueueStoringStrategy q) (queueStore q)+ modifyIORef' (queueCountRef q) (+ (- 1))+ modifyIORef' (dequeueExtractCountRef q) (+ 1) invokeEvent p $ dequeueStat q t' i invokeEvent p $@@ -508,9 +508,9 @@ Event $ \p -> do let t1 = itemStoringTime i t = pointTime p- modifyIORef (queueOutputWaitTimeRef q) $+ modifyIORef' (dequeueWaitTimeRef q) $ addSamplingStats (t - t')- modifyIORef (queueWaitTimeRef q) $+ modifyIORef' (queueWaitTimeRef q) $ addSamplingStats (t - t1) -- | Signal whenever any property of the queue changes.@@ -529,18 +529,18 @@ -- properties and activities using the specified indent. queueSummary :: (Show sm, Show so) => Queue sm qm so qo a -> Int -> Event ShowS queueSummary q indent =- do let sm = queueStoringStrategy q- so = queueOutputStrategy q+ do let sm = enqueueStoringStrategy q+ so = dequeueStrategy q null <- queueNull q count <- queueCount q- storeCount <- queueStoreCount q- outputRequestCount <- queueOutputRequestCount q- outputCount <- queueOutputCount q- storeRate <- queueStoreRate q- outputRequestRate <- queueOutputRequestRate q- outputRate <- queueOutputRate q+ enqueueStoreCount <- enqueueStoreCount q+ dequeueCount <- dequeueCount q+ dequeueExtractCount <- dequeueExtractCount q+ enqueueStoreRate <- enqueueStoreRate q+ dequeueRate <- dequeueRate q+ dequeueExtractRate <- dequeueExtractRate q waitTime <- queueWaitTime q- outputWaitTime <- queueOutputWaitTime q+ dequeueWaitTime <- dequeueWaitTime q let tab = replicate indent ' ' return $ showString tab .@@ -548,7 +548,7 @@ shows sm . showString "\n" . showString tab .- showString "the output (dequeueing) strategy = " .+ showString "the dequeueing (output) strategy = " . shows so . showString "\n" . showString tab .@@ -560,33 +560,33 @@ shows count . showString "\n" . showString tab .- showString "the store count (number of the input items that were stored) = " .- shows storeCount .+ showString "the enqueue store count (number of the input items that were stored) = " .+ shows enqueueStoreCount . showString "\n" . showString tab .- showString "the output request count (number of requests for dequeueing an item) = " .- shows outputRequestCount .+ showString "the dequeue count (number of requests for dequeueing an item) = " .+ shows dequeueCount . showString "\n" . showString tab .- showString "the output count (number of the output items that were dequeued) = " .- shows outputCount .+ showString "the dequeue extract count (number of the output items that were dequeued) = " .+ shows dequeueExtractCount . showString "\n" . showString tab .- showString "the store rate (how many input items were stored per time) = " .- shows storeRate .+ showString "the enqueue store rate (how many input items were stored per time) = " .+ shows enqueueStoreRate . showString "\n" . showString tab .- showString "the output request rate (how many requests for dequeueing per time) = " .- shows outputRequestRate .+ showString "the dequeue rate (how many requests for dequeueing per time) = " .+ shows dequeueRate . showString "\n" . showString tab .- showString "the output rate (how many output items were dequeued per time) = " .- shows outputRate .+ showString "the dequeue extract rate (how many output items were dequeued per time) = " .+ shows dequeueExtractRate . showString "\n" . showString tab . showString "the wait time (when was stored -> when was dequeued) = \n\n" . samplingStatsSummary waitTime (2 + indent) . showString "\n\n" . showString tab .- showString "the output wait time (when was requested for dequeueing -> when was dequeued) = \n\n" .- samplingStatsSummary outputWaitTime (2 + indent)+ showString "the dequeue wait time (when was requested for dequeueing -> when was dequeued) = \n\n" .+ samplingStatsSummary dequeueWaitTime (2 + indent)
Simulation/Aivika/Server.hs view
@@ -18,38 +18,38 @@ -- * Server Properties and Activities serverInitState, serverState,- serverTotalInputTime,+ serverTotalInputWaitTime, serverTotalProcessingTime,- serverTotalOutputTime,- serverInputTime,+ serverTotalOutputWaitTime,+ serverInputWaitTime, serverProcessingTime,- serverOutputTime,- serverInputTimeFactor,- serverProcessingTimeFactor,- serverOutputTimeFactor,+ serverOutputWaitTime,+ serverInputWaitFactor,+ serverProcessingFactor,+ serverOutputWaitFactor, -- * Summary serverSummary, -- * Derived Signals for Properties serverStateChanged, serverStateChanged_,- serverTotalInputTimeChanged,- serverTotalInputTimeChanged_,+ serverTotalInputWaitTimeChanged,+ serverTotalInputWaitTimeChanged_, serverTotalProcessingTimeChanged, serverTotalProcessingTimeChanged_,- serverTotalOutputTimeChanged,- serverTotalOutputTimeChanged_,- serverInputTimeChanged,- serverInputTimeChanged_,+ serverTotalOutputWaitTimeChanged,+ serverTotalOutputWaitTimeChanged_,+ serverInputWaitTimeChanged,+ serverInputWaitTimeChanged_, serverProcessingTimeChanged, serverProcessingTimeChanged_,- serverOutputTimeChanged,- serverOutputTimeChanged_,- serverInputTimeFactorChanged,- serverInputTimeFactorChanged_,- serverProcessingTimeFactorChanged,- serverProcessingTimeFactorChanged_,- serverOutputTimeFactorChanged,- serverOutputTimeFactorChanged_,+ serverOutputWaitTimeChanged,+ serverOutputWaitTimeChanged_,+ serverInputWaitFactorChanged,+ serverInputWaitFactorChanged_,+ serverProcessingFactorChanged,+ serverProcessingFactorChanged_,+ serverOutputWaitFactorChanged,+ serverOutputWaitFactorChanged_, -- * Basic Signals serverInputReceived, serverTaskProcessed,@@ -59,7 +59,9 @@ import Data.IORef import Data.Monoid+ import Control.Monad.Trans+import Control.Arrow import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics@@ -80,17 +82,17 @@ -- ^ The current state of the server. serverProcess :: (s, a) -> Process (s, b), -- ^ Provide @b@ by specified @a@.- serverTotalInputTimeRef :: IORef Double,+ serverTotalInputWaitTimeRef :: IORef Double, -- ^ The counted total time spent in awating the input. serverTotalProcessingTimeRef :: IORef Double, -- ^ The counted total time spent to process the input and prepare the output.- serverTotalOutputTimeRef :: IORef Double,+ serverTotalOutputWaitTimeRef :: IORef Double, -- ^ The counted total time spent for delivering the output.- serverInputTimeRef :: IORef (SamplingStats Double),+ serverInputWaitTimeRef :: IORef (SamplingStats Double), -- ^ The statistics for the time spent in awaiting the input. serverProcessingTimeRef :: IORef (SamplingStats Double), -- ^ The statistics for the time spent to process the input and prepare the output.- serverOutputTimeRef :: IORef (SamplingStats Double),+ serverOutputWaitTimeRef :: IORef (SamplingStats Double), -- ^ The statistics for the time spent for delivering the output. serverInputReceivedSource :: SignalSource a, -- ^ A signal raised when the server recieves a new input to process.@@ -135,12 +137,12 @@ let server = Server { serverInitState = state, serverStateRef = r0, serverProcess = provide,- serverTotalInputTimeRef = r1,+ serverTotalInputWaitTimeRef = r1, serverTotalProcessingTimeRef = r2,- serverTotalOutputTimeRef = r3,- serverInputTimeRef = r4,+ serverTotalOutputWaitTimeRef = r3,+ serverInputWaitTimeRef = r4, serverProcessingTimeRef = r5,- serverOutputTimeRef = r6,+ serverOutputWaitTimeRef = r6, serverInputReceivedSource = s1, serverTaskProcessedSource = s2, serverOutputProvidedSource = s3 }@@ -150,7 +152,23 @@ -- -- The processor updates the internal state of the server. The usual case is when -- the processor is applied only once in a chain of data processing. Otherwise; --- every time the processor is used, the state of the server changes. +-- every time the processor is used, the state of the server changes. Sometimes +-- it can be indeed useful if you want to aggregate the statistics for different +-- servers simultaneously, but it would be more preferable to avoid this.+--+-- If you connect different server processors returned by this function in a chain +-- with help of '>>>' or other category combinator then this chain will act as one +-- whole, where the first server will take a new task only after the last server +-- finishes its current task and requests for the next one from the previous processor +-- in the chain. This is not always that thing you might need.+--+-- To model a sequence of the server processors working independently, you+-- should separate them with help of the 'prefetchProcessor' that plays a role+-- of a small one-place buffer in that case.+--+-- The queue processors usually have the prefetching capabilities per se, where+-- the items are already stored in the queue. Therefore, the server processor+-- should not be prefetched if it is connected directly with the queue processor. serverProcessor :: Server s a b -> Processor a b serverProcessor server = Processor $ \xs -> loop (serverInitState server) Nothing xs@@ -163,8 +181,8 @@ Nothing -> return () Just (t', a', b') -> do liftIO $- do modifyIORef (serverTotalOutputTimeRef server) (+ (t0 - t'))- modifyIORef (serverOutputTimeRef server) $+ do modifyIORef' (serverTotalOutputWaitTimeRef server) (+ (t0 - t'))+ modifyIORef' (serverOutputWaitTimeRef server) $ addSamplingStats (t0 - t') triggerSignal (serverOutputProvidedSource server) (a', b') -- get input@@ -172,8 +190,8 @@ t1 <- liftDynamics time liftEvent $ do liftIO $- do modifyIORef (serverTotalInputTimeRef server) (+ (t1 - t0))- modifyIORef (serverInputTimeRef server) $+ do modifyIORef' (serverTotalInputWaitTimeRef server) (+ (t1 - t0))+ modifyIORef' (serverInputWaitTimeRef server) $ addSamplingStats (t1 - t0) triggerSignal (serverInputReceivedSource server) a -- provide the service@@ -181,9 +199,9 @@ t2 <- liftDynamics time liftEvent $ do liftIO $- do writeIORef (serverStateRef server) s'- modifyIORef (serverTotalProcessingTimeRef server) (+ (t2 - t1))- modifyIORef (serverProcessingTimeRef server) $+ do writeIORef (serverStateRef server) $! s'+ modifyIORef' (serverTotalProcessingTimeRef server) (+ (t2 - t1))+ modifyIORef' (serverProcessingTimeRef server) $ addSamplingStats (t2 - t1) triggerSignal (serverTaskProcessedSource server) (a, b) return (b, loop s' (Just $ (t2, a, b)) xs')@@ -205,27 +223,27 @@ serverStateChanged_ server = mapSignal (const ()) (serverTaskProcessed server) --- | Return the counted total time spent by the server in awaiting the input.+-- | Return the counted total time when the server was locked while awaiting the input. -- -- The value returned changes discretely and it is usually delayed relative -- to the current simulation time. ----- See also 'serverTotalInputTimeChanged' and 'serverTotalInputTimeChanged_'.-serverTotalInputTime :: Server s a b -> Event Double-serverTotalInputTime server =- Event $ \p -> readIORef (serverTotalInputTimeRef server)+-- See also 'serverTotalInputWaitTimeChanged' and 'serverTotalInputWaitTimeChanged_'.+serverTotalInputWaitTime :: Server s a b -> Event Double+serverTotalInputWaitTime server =+ Event $ \p -> readIORef (serverTotalInputWaitTimeRef server) --- | Signal when the 'serverTotalInputTime' property value has changed.-serverTotalInputTimeChanged :: Server s a b -> Signal Double-serverTotalInputTimeChanged server =- mapSignalM (const $ serverTotalInputTime server) (serverTotalInputTimeChanged_ server)+-- | Signal when the 'serverTotalInputWaitTime' property value has changed.+serverTotalInputWaitTimeChanged :: Server s a b -> Signal Double+serverTotalInputWaitTimeChanged server =+ mapSignalM (const $ serverTotalInputWaitTime server) (serverTotalInputWaitTimeChanged_ server) --- | Signal when the 'serverTotalInputTime' property value has changed.-serverTotalInputTimeChanged_ :: Server s a b -> Signal ()-serverTotalInputTimeChanged_ server =+-- | Signal when the 'serverTotalInputWaitTime' property value has changed.+serverTotalInputWaitTimeChanged_ :: Server s a b -> Signal ()+serverTotalInputWaitTimeChanged_ server = mapSignal (const ()) (serverInputReceived server) --- | Return the counted total time spent by the server to process all tasks.+-- | Return the counted total time spent by the server while processing the tasks. -- -- The value returned changes discretely and it is usually delayed relative -- to the current simulation time.@@ -245,48 +263,48 @@ serverTotalProcessingTimeChanged_ server = mapSignal (const ()) (serverTaskProcessed server) --- | Return the counted total time when the server was in the lock state trying+-- | Return the counted total time when the server was locked while trying -- to deliver the output. -- -- The value returned changes discretely and it is usually delayed relative -- to the current simulation time. ----- See also 'serverTotalOutputTimeChanged' and 'serverTotalOutputTimeChanged_'.-serverTotalOutputTime :: Server s a b -> Event Double-serverTotalOutputTime server =- Event $ \p -> readIORef (serverTotalOutputTimeRef server)+-- See also 'serverTotalOutputWaitTimeChanged' and 'serverTotalOutputWaitTimeChanged_'.+serverTotalOutputWaitTime :: Server s a b -> Event Double+serverTotalOutputWaitTime server =+ Event $ \p -> readIORef (serverTotalOutputWaitTimeRef server) --- | Signal when the 'serverTotalOutputTime' property value has changed.-serverTotalOutputTimeChanged :: Server s a b -> Signal Double-serverTotalOutputTimeChanged server =- mapSignalM (const $ serverTotalOutputTime server) (serverTotalOutputTimeChanged_ server)+-- | Signal when the 'serverTotalOutputWaitTime' property value has changed.+serverTotalOutputWaitTimeChanged :: Server s a b -> Signal Double+serverTotalOutputWaitTimeChanged server =+ mapSignalM (const $ serverTotalOutputWaitTime server) (serverTotalOutputWaitTimeChanged_ server) --- | Signal when the 'serverTotalOutputTime' property value has changed.-serverTotalOutputTimeChanged_ :: Server s a b -> Signal ()-serverTotalOutputTimeChanged_ server =+-- | Signal when the 'serverTotalOutputWaitTime' property value has changed.+serverTotalOutputWaitTimeChanged_ :: Server s a b -> Signal ()+serverTotalOutputWaitTimeChanged_ server = mapSignal (const ()) (serverOutputProvided server) --- | Return the statistics of the time spent by the server in awaiting the input.+-- | Return the statistics of the time when the server was locked while awaiting the input. -- -- The value returned changes discretely and it is usually delayed relative -- to the current simulation time. ----- See also 'serverInputTimeChanged' and 'serverInputTimeChanged_'.-serverInputTime :: Server s a b -> Event (SamplingStats Double)-serverInputTime server =- Event $ \p -> readIORef (serverInputTimeRef server)+-- See also 'serverInputWaitTimeChanged' and 'serverInputWaitTimeChanged_'.+serverInputWaitTime :: Server s a b -> Event (SamplingStats Double)+serverInputWaitTime server =+ Event $ \p -> readIORef (serverInputWaitTimeRef server) --- | Signal when the 'serverInputTime' property value has changed.-serverInputTimeChanged :: Server s a b -> Signal (SamplingStats Double)-serverInputTimeChanged server =- mapSignalM (const $ serverInputTime server) (serverInputTimeChanged_ server)+-- | Signal when the 'serverInputWaitTime' property value has changed.+serverInputWaitTimeChanged :: Server s a b -> Signal (SamplingStats Double)+serverInputWaitTimeChanged server =+ mapSignalM (const $ serverInputWaitTime server) (serverInputWaitTimeChanged_ server) --- | Signal when the 'serverInputTime' property value has changed.-serverInputTimeChanged_ :: Server s a b -> Signal ()-serverInputTimeChanged_ server =+-- | Signal when the 'serverInputWaitTime' property value has changed.+serverInputWaitTimeChanged_ :: Server s a b -> Signal ()+serverInputWaitTimeChanged_ server = mapSignal (const ()) (serverInputReceived server) --- | Return the statistics of the time spent by the server to process the tasks.+-- | Return the statistics of the time spent by the server while processing the tasks. -- -- The value returned changes discretely and it is usually delayed relative -- to the current simulation time.@@ -306,25 +324,25 @@ serverProcessingTimeChanged_ server = mapSignal (const ()) (serverTaskProcessed server) --- | Return the statistics of the time when the server was in the lock state trying+-- | Return the statistics of the time when the server was locked while trying -- to deliver the output. -- -- The value returned changes discretely and it is usually delayed relative -- to the current simulation time. ----- See also 'serverOutputTimeChanged' and 'serverOutputTimeChanged_'.-serverOutputTime :: Server s a b -> Event (SamplingStats Double)-serverOutputTime server =- Event $ \p -> readIORef (serverOutputTimeRef server)+-- See also 'serverOutputWaitTimeChanged' and 'serverOutputWaitTimeChanged_'.+serverOutputWaitTime :: Server s a b -> Event (SamplingStats Double)+serverOutputWaitTime server =+ Event $ \p -> readIORef (serverOutputWaitTimeRef server) --- | Signal when the 'serverOutputTime' property value has changed.-serverOutputTimeChanged :: Server s a b -> Signal (SamplingStats Double)-serverOutputTimeChanged server =- mapSignalM (const $ serverOutputTime server) (serverOutputTimeChanged_ server)+-- | Signal when the 'serverOutputWaitTime' property value has changed.+serverOutputWaitTimeChanged :: Server s a b -> Signal (SamplingStats Double)+serverOutputWaitTimeChanged server =+ mapSignalM (const $ serverOutputWaitTime server) (serverOutputWaitTimeChanged_ server) --- | Signal when the 'serverOutputTime' property value has changed.-serverOutputTimeChanged_ :: Server s a b -> Signal ()-serverOutputTimeChanged_ server =+-- | Signal when the 'serverOutputWaitTime' property value has changed.+serverOutputWaitTimeChanged_ :: Server s a b -> Signal ()+serverOutputWaitTimeChanged_ server = mapSignal (const ()) (serverOutputProvided server) -- | It returns the factor changing from 0 to 1, which estimates how often@@ -333,29 +351,29 @@ -- This factor is calculated as -- -- @--- totalInputTime \/ (totalInputTime + totalProcessingTime + totalOutputTime)+-- totalInputWaitTime \/ (totalInputWaitTime + totalProcessingTime + totalOutputWaitTime) -- @ -- -- As before in this module, the value returned changes discretely and -- it is usually delayed relative to the current simulation time. ----- See also 'serverInputTimeFactorChanged' and 'serverInputTimeFactorChanged_'.-serverInputTimeFactor :: Server s a b -> Event Double-serverInputTimeFactor server =+-- See also 'serverInputWaitFactorChanged' and 'serverInputWaitFactorChanged_'.+serverInputWaitFactor :: Server s a b -> Event Double+serverInputWaitFactor server = Event $ \p ->- do x1 <- readIORef (serverTotalInputTimeRef server)+ do x1 <- readIORef (serverTotalInputWaitTimeRef server) x2 <- readIORef (serverTotalProcessingTimeRef server)- x3 <- readIORef (serverTotalOutputTimeRef server)+ x3 <- readIORef (serverTotalOutputWaitTimeRef server) return (x1 / (x1 + x2 + x3)) --- | Signal when the 'serverInputTimeFactor' property value has changed.-serverInputTimeFactorChanged :: Server s a b -> Signal Double-serverInputTimeFactorChanged server =- mapSignalM (const $ serverInputTimeFactor server) (serverInputTimeFactorChanged_ server)+-- | Signal when the 'serverInputWaitFactor' property value has changed.+serverInputWaitFactorChanged :: Server s a b -> Signal Double+serverInputWaitFactorChanged server =+ mapSignalM (const $ serverInputWaitFactor server) (serverInputWaitFactorChanged_ server) --- | Signal when the 'serverInputTimeFactor' property value has changed.-serverInputTimeFactorChanged_ :: Server s a b -> Signal ()-serverInputTimeFactorChanged_ server =+-- | Signal when the 'serverInputWaitFactor' property value has changed.+serverInputWaitFactorChanged_ :: Server s a b -> Signal ()+serverInputWaitFactorChanged_ server = mapSignal (const ()) (serverInputReceived server) <> mapSignal (const ()) (serverTaskProcessed server) <> mapSignal (const ()) (serverOutputProvided server)@@ -366,29 +384,29 @@ -- This factor is calculated as -- -- @--- totalProcessingTime \/ (totalInputTime + totalProcessingTime + totalOutputTime)+-- totalProcessingTime \/ (totalInputWaitTime + totalProcessingTime + totalOutputWaitTime) -- @ -- -- As before in this module, the value returned changes discretely and -- it is usually delayed relative to the current simulation time. ----- See also 'serverProcessingTimeFactorChanged' and 'serverProcessingTimeFactorChanged_'.-serverProcessingTimeFactor :: Server s a b -> Event Double-serverProcessingTimeFactor server =+-- See also 'serverProcessingFactorChanged' and 'serverProcessingFactorChanged_'.+serverProcessingFactor :: Server s a b -> Event Double+serverProcessingFactor server = Event $ \p ->- do x1 <- readIORef (serverTotalInputTimeRef server)+ do x1 <- readIORef (serverTotalInputWaitTimeRef server) x2 <- readIORef (serverTotalProcessingTimeRef server)- x3 <- readIORef (serverTotalOutputTimeRef server)+ x3 <- readIORef (serverTotalOutputWaitTimeRef server) return (x2 / (x1 + x2 + x3)) --- | Signal when the 'serverProcessingTimeFactor' property value has changed.-serverProcessingTimeFactorChanged :: Server s a b -> Signal Double-serverProcessingTimeFactorChanged server =- mapSignalM (const $ serverProcessingTimeFactor server) (serverProcessingTimeFactorChanged_ server)+-- | Signal when the 'serverProcessingFactor' property value has changed.+serverProcessingFactorChanged :: Server s a b -> Signal Double+serverProcessingFactorChanged server =+ mapSignalM (const $ serverProcessingFactor server) (serverProcessingFactorChanged_ server) --- | Signal when the 'serverProcessingTimeFactor' property value has changed.-serverProcessingTimeFactorChanged_ :: Server s a b -> Signal ()-serverProcessingTimeFactorChanged_ server =+-- | Signal when the 'serverProcessingFactor' property value has changed.+serverProcessingFactorChanged_ :: Server s a b -> Signal ()+serverProcessingFactorChanged_ server = mapSignal (const ()) (serverInputReceived server) <> mapSignal (const ()) (serverTaskProcessed server) <> mapSignal (const ()) (serverOutputProvided server)@@ -399,29 +417,29 @@ -- This factor is calculated as -- -- @--- totalOutputTime \/ (totalInputTime + totalProcessingTime + totalOutputTime)+-- totalOutputWaitTime \/ (totalInputWaitTime + totalProcessingTime + totalOutputWaitTime) -- @ -- -- As before in this module, the value returned changes discretely and -- it is usually delayed relative to the current simulation time. ----- See also 'serverOutputTimeFactorChanged' and 'serverOutputTimeFactorChanged_'.-serverOutputTimeFactor :: Server s a b -> Event Double-serverOutputTimeFactor server =+-- See also 'serverOutputWaitFactorChanged' and 'serverOutputWaitFactorChanged_'.+serverOutputWaitFactor :: Server s a b -> Event Double+serverOutputWaitFactor server = Event $ \p ->- do x1 <- readIORef (serverTotalInputTimeRef server)+ do x1 <- readIORef (serverTotalInputWaitTimeRef server) x2 <- readIORef (serverTotalProcessingTimeRef server)- x3 <- readIORef (serverTotalOutputTimeRef server)+ x3 <- readIORef (serverTotalOutputWaitTimeRef server) return (x3 / (x1 + x2 + x3)) --- | Signal when the 'serverOutputTimeFactor' property value has changed.-serverOutputTimeFactorChanged :: Server s a b -> Signal Double-serverOutputTimeFactorChanged server =- mapSignalM (const $ serverOutputTimeFactor server) (serverOutputTimeFactorChanged_ server)+-- | Signal when the 'serverOutputWaitFactor' property value has changed.+serverOutputWaitFactorChanged :: Server s a b -> Signal Double+serverOutputWaitFactorChanged server =+ mapSignalM (const $ serverOutputWaitFactor server) (serverOutputWaitFactorChanged_ server) --- | Signal when the 'serverOutputTimeFactor' property value has changed.-serverOutputTimeFactorChanged_ :: Server s a b -> Signal ()-serverOutputTimeFactorChanged_ server =+-- | Signal when the 'serverOutputWaitFactor' property value has changed.+serverOutputWaitFactorChanged_ :: Server s a b -> Signal ()+serverOutputWaitFactorChanged_ server = mapSignal (const ()) (serverInputReceived server) <> mapSignal (const ()) (serverTaskProcessed server) <> mapSignal (const ()) (serverOutputProvided server)@@ -450,37 +468,37 @@ serverSummary :: Server s a b -> Int -> Event ShowS serverSummary server indent = Event $ \p ->- do tx1 <- readIORef (serverTotalInputTimeRef server)+ do tx1 <- readIORef (serverTotalInputWaitTimeRef server) tx2 <- readIORef (serverTotalProcessingTimeRef server)- tx3 <- readIORef (serverTotalOutputTimeRef server)+ tx3 <- readIORef (serverTotalOutputWaitTimeRef server) let xf1 = tx1 / (tx1 + tx2 + tx3) xf2 = tx2 / (tx1 + tx2 + tx3) xf3 = tx3 / (tx1 + tx2 + tx3)- xs1 <- readIORef (serverInputTimeRef server)+ xs1 <- readIORef (serverInputWaitTimeRef server) xs2 <- readIORef (serverProcessingTimeRef server)- xs3 <- readIORef (serverOutputTimeRef server)+ xs3 <- readIORef (serverOutputWaitTimeRef server) let tab = replicate indent ' ' return $ showString tab .- showString "total input time (in awaiting the input) = " . shows tx1 .+ showString "total input wait time (locked while awaiting the input) = " . shows tx1 . showString "\n" . showString tab . showString "total processing time = " . shows tx2 . showString "\n" . showString tab .- showString "total output time (to deliver the output) = " . shows tx3 .+ showString "total output wait time (locked while delivering the output) = " . shows tx3 . showString "\n\n" . showString tab .- showString "input time factor (from 0 to 1) = " . shows xf1 .+ showString "input wait factor (from 0 to 1) = " . shows xf1 . showString "\n" . showString tab .- showString "processing time factor (from 0 to 1) = " . shows xf2 .+ showString "processing factor (from 0 to 1) = " . shows xf2 . showString "\n" . showString tab .- showString "output time factor (from 0 to 1) = " . shows xf3 .+ showString "output wait factor (from 0 to 1) = " . shows xf3 . showString "\n\n" . showString tab .- showString "input time:\n\n" .+ showString "input wait time (locked while awaiting the input):\n\n" . samplingStatsSummary xs1 (2 + indent) . showString "\n\n" . showString tab .@@ -488,5 +506,5 @@ samplingStatsSummary xs2 (2 + indent) . showString "\n\n" . showString tab .- showString "output time:\n\n" .+ showString "output wait time (locked while delivering the output):\n\n" . samplingStatsSummary xs3 (2 + indent)
Simulation/Aivika/Signal.hs view
@@ -40,6 +40,7 @@ SignalHistory, signalHistorySignal, newSignalHistory,+ newSignalHistoryStartingWith, readSignalHistory, -- * Signalable Computations Signalable(..),
+ Simulation/Aivika/Statistics/Accumulator.hs view
@@ -0,0 +1,44 @@++-- |+-- Module : Simulation.Aivika.Statistics.Accumulator+-- Copyright : Copyright (c) 2009-2014, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- This small utility module allows accumulating the timing statistics based on 'Signalable' data+-- such as the queue size or the number of lost items in the queue.+--++module Simulation.Aivika.Statistics.Accumulator+ (-- * Timing Statistics Accumulator+ TimingStatsAccumulator,+ newTimingStatsAccumulator,+ timingStatsAccumulated) where++import Simulation.Aivika.Simulation+import Simulation.Aivika.Dynamics+import Simulation.Aivika.Event+import Simulation.Aivika.Ref+import Simulation.Aivika.Statistics+import Simulation.Aivika.Signal++-- | Represents an accumulator for the timing statistics.+newtype TimingStatsAccumulator a =+ TimingStatsAccumulator { timingStatsAccumulatedRef :: Ref (TimingStats a) }++-- | Return the accumulated statistics.+timingStatsAccumulated :: TimingStatsAccumulator a -> Event (TimingStats a)+timingStatsAccumulated = readRef . timingStatsAccumulatedRef++-- | Start gathering the timing statistics from the current simulation time. +newTimingStatsAccumulator :: TimingData a => Signalable a -> Event (TimingStatsAccumulator a)+newTimingStatsAccumulator x =+ do t0 <- liftDynamics time+ a0 <- readSignalable x+ r <- liftSimulation $ newRef (returnTimingStats t0 a0)+ handleSignal_ (signalableChanged x) $ \a ->+ do t <- liftDynamics time+ modifyRef r $ addTimingStats t a+ return TimingStatsAccumulator { timingStatsAccumulatedRef = r }
Simulation/Aivika/Stream.hs view
@@ -25,6 +25,8 @@ splitStreamPrioritising, -- * Specifying Identifier streamUsingId,+ -- * Prefetching Stream+ prefetchStream, -- * Memoizing, Zipping and Uzipping Stream memoStream, zipStreamSeq,@@ -232,7 +234,7 @@ -- | Replace the 'Left' values. replaceLeftStream :: Stream (Either a b) -> Stream c -> Stream (Either c b)-replaceLeftStream (Cons sab) (ys0 @ (Cons sc)) = Cons z where+replaceLeftStream (Cons sab) (ys0 @ ~(Cons sc)) = Cons z where z = do (a, xs) <- sab case a of Left _ ->@@ -243,7 +245,7 @@ -- | Replace the 'Right' values. replaceRightStream :: Stream (Either a b) -> Stream c -> Stream (Either a c)-replaceRightStream (Cons sab) (ys0 @ (Cons sc)) = Cons z where+replaceRightStream (Cons sab) (ys0 @ ~(Cons sc)) = Cons z where z = do (a, xs) <- sab case a of Right _ ->@@ -332,12 +334,14 @@ concatQueuedStreams s streams = Cons z where z = do reading <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1) writing <- liftSimulation $ newResourceWithMaxCount s 1 (Just 1)+ conting <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1) ref <- liftIO $ newIORef Nothing let writer p = do (a, xs) <- runStream p requestResource writing liftIO $ writeIORef ref (Just a) releaseResource reading+ requestResource conting writer xs reader = do requestResource reading@@ -346,7 +350,9 @@ releaseResource writing return a forM_ streams $ spawnProcess CancelTogether . writer- runStream $ repeatProcess reader+ a <- reader+ let xs = repeatProcess (releaseResource conting >> reader)+ return (a, xs) -- | Concatenate the input priority streams producing one output stream. concatPriorityStreams :: PriorityQueueStrategy s q p@@ -359,12 +365,14 @@ concatPriorityStreams s streams = Cons z where z = do reading <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1) writing <- liftSimulation $ newResourceWithMaxCount s 1 (Just 1)+ conting <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1) ref <- liftIO $ newIORef Nothing let writer p = do ((priority, a), xs) <- runStream p requestResourceWithPriority writing priority liftIO $ writeIORef ref (Just a) releaseResource reading+ requestResource conting writer xs reader = do requestResource reading@@ -373,7 +381,9 @@ releaseResource writing return a forM_ streams $ spawnProcess CancelTogether . writer- runStream $ repeatProcess reader+ a <- reader+ let xs = repeatProcess (releaseResource conting >> reader)+ return (a, xs) -- | Merge two streams applying the 'FCFS' strategy for enqueuing the input data. mergeStreams :: Stream a -> Stream a -> Stream a@@ -419,7 +429,7 @@ -- | Consume the stream. It returns a process that infinitely reads data -- from the stream and then redirects them to the provided function.--- It is useful for modeling the process of enqueuing data in the queue+-- It is useful for modeling the process of enqueueing data in the queue -- from the input stream. consumeStream :: (a -> Process ()) -> Stream a -> Process () consumeStream f s = p s where@@ -436,3 +446,29 @@ p (Cons s) = do (a, xs) <- s p xs +-- | Prefetch the input stream requesting for one more data item in advance +-- while the last received item is not yet fully processed in the chain of +-- streams, usually by the processors.+--+-- You can think of this as the prefetched stream could place its latest +-- data item in some temporary space for later use, which is very useful +-- for modeling a sequence of separate and independent work places.+prefetchStream :: Stream a -> Stream a+prefetchStream s = Cons z where+ z = do reading <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1)+ writing <- liftSimulation $ newResourceWithMaxCount FCFS 1 (Just 1)+ ref <- liftIO $ newIORef Nothing+ let writer p =+ do (a, xs) <- runStream p+ requestResource writing+ liftIO $ writeIORef ref (Just a)+ releaseResource reading+ writer xs+ reader =+ do requestResource reading+ Just a <- liftIO $ readIORef ref+ liftIO $ writeIORef ref Nothing+ releaseResource writing+ return a+ spawnProcess CancelTogether $ writer s+ runStream $ repeatProcess reader
Simulation/Aivika/Stream/Random.hs view
@@ -7,12 +7,14 @@ -- Stability : experimental -- Tested with: GHC 7.6.3 ----- This module defines random streams of data, which are useful+-- This module defines random streams of events, which are useful -- for describing the input of the model. -- module Simulation.Aivika.Stream.Random- (randomUniformStream,+ (-- * Stream of Random Events+ randomStream,+ randomUniformStream, randomNormalStream, randomExponentialStream, randomErlangStream,@@ -21,69 +23,99 @@ import System.Random +import Control.Monad import Control.Monad.Trans import Simulation.Aivika.Parameter import Simulation.Aivika.Parameter.Random import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics+import Simulation.Aivika.Event import Simulation.Aivika.Process+import Simulation.Aivika.Processor import Simulation.Aivika.Stream+import Simulation.Aivika.Statistics+import Simulation.Aivika.Ref+import Simulation.Aivika.Arrival +-- | Return a sream of random events that arrive with the specified delay.+randomStream :: Parameter Double -> Stream Arrival+randomStream delay = Cons z0 where+ z0 =+ do t0 <- liftDynamics time+ loop t0+ loop t0 =+ do t1 <- liftDynamics time+ when (t1 /= t0) $+ error $+ "The time of requesting for a new random event is different from " +++ "the time when the previous event has arrived. Probably, your model " +++ "contains a logical error. The random events should be requested permanently. " +++ "At least, they can be lost, for example, when trying to enqueue them, but " +++ "the random stream itself must always work: randomStream."+ delay <- liftParameter delay+ holdProcess delay+ t2 <- liftDynamics time+ let arrival = Arrival { arrivalTime = t2,+ arrivalDelay = delay }+ return (arrival, Cons $ loop t2)+ -- | Create a new stream with delays distributed uniformly.-randomUniformStream :: Double -- ^ the minimum delay- -> Double -- ^ the maximum delay- -> Stream Double -- ^ the stream of delays-randomUniformStream min max = Cons z where- z = do delay <- liftParameter $ randomUniform min max- holdProcess delay- return (delay, randomUniformStream min max)+randomUniformStream :: Double+ -- ^ the minimum delay+ -> Double+ -- ^ the maximum delay+ -> Stream Arrival+ -- ^ the stream of random events+randomUniformStream min max =+ randomStream $ randomUniform min max -- | Create a new stream with delays distributed normally.-randomNormalStream :: Double -- ^ the mean delay- -> Double -- ^ the delay deviation- -> Stream Double -- ^ the stream of delays-randomNormalStream mu nu = Cons z where- z = do delay <- liftParameter $ randomNormal mu nu- holdProcess delay- return (delay, randomNormalStream mu nu)+randomNormalStream :: Double+ -- ^ the mean delay+ -> Double+ -- ^ the delay deviation+ -> Stream Arrival+ -- ^ the stream of random events+randomNormalStream mu nu =+ randomStream $ randomNormal mu nu -- | Return a new stream with delays distibuted exponentially with the specified mean -- (the reciprocal of the rate). randomExponentialStream :: Double -- ^ the mean delay (the reciprocal of the rate)- -> Stream Double- -- ^ the stream of delays-randomExponentialStream mu = Cons z where- z = do delay <- liftParameter $ randomExponential mu- holdProcess delay- return (delay, randomExponentialStream mu)+ -> Stream Arrival+ -- ^ the stream of random events+randomExponentialStream mu =+ randomStream $ randomExponential mu -- | Return a new stream with delays having the Erlang distribution with the specified -- scale (the reciprocal of the rate) and shape parameters.-randomErlangStream :: Double -- ^ the scale (the reciprocal of the rate)- -> Int -- ^ the shape- -> Stream Double -- ^ the stream of delays-randomErlangStream beta m = Cons z where- z = do delay <- liftParameter $ randomErlang beta m- holdProcess delay- return (delay, randomErlangStream beta m)+randomErlangStream :: Double+ -- ^ the scale (the reciprocal of the rate)+ -> Int+ -- ^ the shape+ -> Stream Arrival+ -- ^ the stream of random events+randomErlangStream beta m =+ randomStream $ randomErlang beta m -- | Return a new stream with delays having the Poisson distribution with -- the specified mean.-randomPoissonStream :: Double -- ^ the mean delay- -> Stream Int -- ^ the stream of delays-randomPoissonStream mu = Cons z where- z = do delay <- liftParameter $ randomPoisson mu- holdProcess $ fromIntegral delay- return (delay, randomPoissonStream mu)+randomPoissonStream :: Double+ -- ^ the mean delay+ -> Stream Arrival+ -- ^ the stream of random events+randomPoissonStream mu =+ randomStream $ fmap fromIntegral $ randomPoisson mu -- | Return a new stream with delays having the binomial distribution with the specified -- probability and trials.-randomBinomialStream :: Double -- ^ the probability- -> Int -- ^ the number of trials- -> Stream Int -- ^ the stream of delays-randomBinomialStream prob trials = Cons z where- z = do delay <- liftParameter $ randomBinomial prob trials- holdProcess $ fromIntegral delay- return (delay, randomBinomialStream prob trials)+randomBinomialStream :: Double+ -- ^ the probability+ -> Int+ -- ^ the number of trials+ -> Stream Arrival+ -- ^ the stream of random events+randomBinomialStream prob trials =+ randomStream $ fmap fromIntegral $ randomBinomial prob trials
Simulation/Aivika/Task.hs view
@@ -87,7 +87,7 @@ -- | Cancel the task. cancelTask :: Task a -> Event () cancelTask t =- cancelProcessUsingId (taskId t)+ cancelProcessWithId (taskId t) -- | Test whether the task was cancelled. taskCancelled :: Task a -> Event Bool
Simulation/Aivika/Var.hs view
@@ -61,11 +61,10 @@ -- | Read the value of a variable. ----- It is safe to run the resulting computation with help of the 'runEvent'--- function using modes 'IncludingCurrentEventsOrFromPast' and--- 'IncludingEarlierEventsOrFromPast', which is necessary if you are going--- to use the variable in the differential or difference equations. Only--- it is preferrable if the variable is not updated twice+-- It is safe to run the resulting computation with help of the 'runEventWith'+-- function using modes 'CurrentEventsOrFromPast' and 'EarlierEventsOrFromPast', +-- which is necessary if you are going to use the variable in the differential +-- or difference equations. Only it is preferrable if the variable is not updated twice -- in the same integration time point; otherwise, different values can be returned -- for the same point. readVar :: Var a -> Event a
Simulation/Aivika/Var/Unboxed.hs view
@@ -61,11 +61,10 @@ -- | Read the value of a variable. ----- It is safe to run the resulting computation with help of the 'runEvent'--- function using modes 'IncludingCurrentEventsOrFromPast' and--- 'IncludingEarlierEventsOrFromPast', which is necessary if you are going--- to use the variable in the differential or difference equations. Only--- it is preferrable if the variable is not updated twice+-- It is safe to run the resulting computation with help of the 'runEventWith'+-- function using modes 'CurrentEventsOrFromPast' and 'EarlierEventsOrFromPast', +-- which is necessary if you are going to use the variable in the differential +-- or difference equations. Only it is preferrable if the variable is not updated twice -- in the same integration time point; otherwise, different values can be returned -- for the same point. readVar :: Unboxed a => Var a -> Event a
aivika.cabal view
@@ -1,17 +1,20 @@ name: aivika-version: 1.0+version: 1.1 synopsis: A multi-paradigm simulation library description:- Aivika is a multi-paradigm simulation library which has - the following features:+ Aivika is a multi-paradigm simulation library with a strong emphasis+ on the Discrete Event Simulation (DES) in the first order and + System Dynamics (SD) in the second one. .+ The library has the following features:+ . * allows defining recursive stochastic differential equations of System Dynamics (unordered as in maths via the recursive do-notation); .- * has a basic support of the event-driven paradigm of - the Discrete Event Simulation (DES);+ * supports the event-driven paradigm of DES as a basic core for + implementing other paradigms; .- * has a basic support of the process-oriented paradigm of DES+ * supports extensively the process-oriented paradigm of DES with an ability to resume, suspend and cancel the discontinuous processes; .@@ -22,18 +25,21 @@ or use the predefined queue strategies); . * allows defining an infinite stream of data based on the- process-oriented computation (designed but not tested in- anyway - please be very careful when using it);+ process-oriented computation, where we can define a complex enough+ behaviour just in a few lines of code; . * allows defining processors (actually, the Haskell arrows) that- operate on infinite streams of data (designed but not tested- in anyway - please be very careful when using them);+ operate on the infinite streams of data, because of which some models+ can remind of their high-level graphical representation on the+ diagram used by visual simulation software tools; . * supports the activity-oriented paradigm of DES; . * supports the basic constructs for the agent-based modeling; .- * allows creating combined discrete-continuous models;+ * allows creating combined discrete-continuous models as all parts+ of the library are very well integrated and this is reflected+ directly in the type system; . * the arrays of simulation variables are inherently supported (this is mostly a feature of Haskell itself);@@ -48,7 +54,8 @@ * allows gathering statistics in time points; . * hides the technical details in high-level simulation monads- (three of them support the recursive do-notation).+ and even one arrow (some of these monads support the recursive + do-notation). . Aivika itself is a light-weight engine with minimal dependencies. However, it has additional packages Aivika Experiment [1] and @@ -70,25 +77,9 @@ All three libraries were tested on Linux, Windows and OS X. . Please read the PDF document An Introduction to - Aivika Simulation Library [3] for more details (a little outdated). - This document is included in the distributive of Aivika but - you can usually find a more recent version by the link provided.- .- P.S.- .- Two items, streams and processors, are not yet tested. This is a- goal for the future version of Aivika. The main reason why I ever uploaded- my three packages is that the Aivika Experiment Chart package- was broken in its dependencies, namely, when using the charting- library. So, I decided to provide the compilable packages again.- .- Although I would like to say that the mentioned streams and processors- will be the main improvement in the future version as they actually- allow defining some DES models on a very high level as you would define- them with help of diagrams.- .- Also the queues and server are not tested carefully. Use at your own- risk. At least, the infinite queue seems to be working.+ Aivika Simulation Library [3] for more details, although it is+ outdated and contains a very basic description only. The most powerful+ features of Aivika are not yet described in this PDF document. . \[1] <http://hackage.haskell.org/package/aivika-experiment> .@@ -116,8 +107,11 @@ examples/MachRep2.hs examples/MachRep3.hs examples/Furnace.hs+ examples/SimpleWorkflow.hs examples/TimeOut.hs examples/TimeOutInt.hs+ examples/TimeOutWait.hs+ examples/WorkflowLoop.hs examples/README data-files: doc/aivika.pdf@@ -126,6 +120,7 @@ exposed-modules: Simulation.Aivika Simulation.Aivika.Agent+ Simulation.Aivika.Arrival Simulation.Aivika.Cont Simulation.Aivika.DoubleLinkedList Simulation.Aivika.Dynamics@@ -152,6 +147,7 @@ Simulation.Aivika.Simulation Simulation.Aivika.Specs Simulation.Aivika.Statistics+ Simulation.Aivika.Statistics.Accumulator Simulation.Aivika.Stream Simulation.Aivika.Stream.Random Simulation.Aivika.SystemDynamics
examples/BassDiffusion.hs view
@@ -18,11 +18,6 @@ spcMethod = RungeKutta4, spcGeneratorType = SimpleGenerator } -randomTrue :: Double -> Parameter Bool-randomTrue p =- do x <- randomUniform 0 1- return (x <= p)- data Person = Person { personAgent :: Agent, personPotentialAdopter :: AgentState, personAdopter :: AgentState }@@ -89,10 +84,10 @@ adopters <- newRef 0 ps <- createPersons definePersons ps potentialAdopters adopters- runEventInStartTime IncludingCurrentEvents $+ runEventInStartTime $ activatePersons ps runDynamicsInIntegTimes $- runEvent IncludingCurrentEvents $+ runEvent $ do i1 <- readRef potentialAdopters i2 <- readRef adopters return [i1, i2]
examples/Furnace.hs view
@@ -273,23 +273,23 @@ do furnace <- newFurnace -- initialize the furnace and start its iterating in start time- runEventInStartTime IncludingCurrentEvents $+ runEventInStartTime $ do initializeFurnace furnace startIteratingFurnace furnace -- generate randomly new input ingots- runProcessInStartTime IncludingCurrentEvents $+ runProcessInStartTime $ inputProcess furnace -- load permanently the input ingots in the furnace- runProcessInStartTime IncludingCurrentEvents $+ runProcessInStartTime $ loadingProcess furnace -- run the model in the final time point- runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do -- the ingots- c0 <- queueStoreCount (furnaceQueue furnace)- c1 <- queueOutputCount (furnaceQueue furnace)+ c0 <- enqueueStoreCount (furnaceQueue furnace)+ c1 <- dequeueCount (furnaceQueue furnace) c2 <- readRef (furnaceReadyCount furnace) liftIO $ do
examples/MachRep1.hs view
@@ -46,10 +46,10 @@ holdProcess repairTime machine - runProcessInStartTime IncludingCurrentEvents machine- runProcessInStartTime IncludingCurrentEvents machine+ runProcessInStartTime machine+ runProcessInStartTime machine - runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do x <- readRef totalUpTime y <- liftParameter stoptime return $ x / (2 * y)
examples/MachRep1EventDriven.hs view
@@ -57,13 +57,13 @@ let t = startUpTime + upTime enqueueEvent t $ machineBroken startUpTime - runEventInStartTime IncludingCurrentEvents $+ runEventInStartTime $ do -- start the first machine machineRepaired -- start the second machine machineRepaired - runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do x <- readRef totalUpTime y <- liftParameter stoptime return $ x / (2 * y)
examples/MachRep1TimeDriven.hs view
@@ -94,14 +94,14 @@ m2 <- machine -- start the time-driven simulation of the machines- runEventInStartTime IncludingCurrentEvents $+ runEventInStartTime $ -- in the integration time points enqueueEventWithIntegTimes $ do m1 m2 -- return the result in the stop time- runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do x <- readRef totalUpTime y <- liftParameter stoptime return $ x / (2 * y)
examples/MachRep2.hs view
@@ -70,10 +70,10 @@ machine - runProcessInStartTime IncludingCurrentEvents machine- runProcessInStartTime IncludingCurrentEvents machine+ runProcessInStartTime machine+ runProcessInStartTime machine - runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do x <- readRef totalUpTime y <- liftParameter stoptime n <- readRef nRep
examples/MachRep3.hs view
@@ -70,13 +70,13 @@ machine pid - runProcessInStartTimeUsingId IncludingCurrentEvents+ runProcessInStartTimeUsingId pid1 (machine pid2) - runProcessInStartTimeUsingId IncludingCurrentEvents+ runProcessInStartTimeUsingId pid2 (machine pid1) - runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do x <- readRef totalUpTime y <- liftDynamics time return $ x / (2 * y)
+ examples/SimpleWorkflow.hs view
@@ -0,0 +1,157 @@++-- This is a model of the workflow with originally two work places. Also there are two finite queues.+--+-- It is described in different sources [1, 2]. So, this is chapter 7 of [2].+--+-- [1] { add a foreign source in English }+--+-- [2] Труб И.И., Объектно-ориентированное моделирование на C++: Учебный курс. - СПб.: Питер, 2006++import Prelude hiding (id, (.)) ++import Control.Monad+import Control.Monad.Trans+import Control.Arrow+import Control.Category (id, (.))++import Simulation.Aivika+import Simulation.Aivika.Queue++-- | The simulation specs.+specs = Specs { spcStartTime = 0.0,+ spcStopTime = 300.0,+ spcDT = 0.1,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }++-- the mean delay of the input arrivals distributed exponentially+meanOrderDelay = 0.4 ++-- the capacity of the queue before the first work places+queueMaxCount1 = 4++-- the capacity of the queue before the second work places+queueMaxCount2 = 2++-- the mean processing time distributed exponentially in+-- the first work places+meanProcessingTime1 = 0.25++-- the mean processing time distributed exponentially in+-- the second work places+meanProcessingTime2 = 0.5++-- the number of the first work places+-- (in parallel but the commented code allocates them sequentially)+workplaceCount1 = 1++-- the number of the second work places+-- (in parallel but the commented code allocates them sequentially)+workplaceCount2 = 1++-- create an accumulator to gather the queue size statistics +newQueueSizeAccumulator queue =+ newTimingStatsAccumulator $+ Signalable (queueCount queue) (queueCountChanged_ queue)++-- create a workflow with the exponential processing time+newWorkplaceExponential meanTime =+ newServer $ \a ->+ do holdProcess =<<+ (liftParameter $+ randomExponential meanTime)+ return a++-- interpose the prefetch processor between two processors+interposePrefetchProcessor x y = + x >>> prefetchProcessor >>> y++model :: Simulation ()+model = do+ -- it will gather the statistics of the processing time+ arrivalTimer <- newArrivalTimer+ -- define a stream of input events+ let inputStream = randomExponentialStream meanOrderDelay + -- create a queue before the first work place+ queue1 <- newFCFSQueue queueMaxCount1+ -- create a queue before the second work place+ queue2 <- newFCFSQueue queueMaxCount2+ -- the first queue size statistics+ queueSizeAcc1 <- + runEventInStartTime $+ newQueueSizeAccumulator queue1+ -- the second queue size statistics+ queueSizeAcc2 <- + runEventInStartTime $+ newQueueSizeAccumulator queue2+ -- create the first work places, i.e. the "servers"+ workplace1s <- forM [1 .. workplaceCount1] $ \_ ->+ newWorkplaceExponential meanProcessingTime1+ -- create the second work places, i.e. the "servers"+ workplace2s <- forM [1 .. workplaceCount2] $ \_ ->+ newWorkplaceExponential meanProcessingTime2+ -- processor for the queue before the first work place+ let queueProcessor1 =+ queueProcessor+ (\a -> liftEvent $ enqueueOrLost_ queue1 a)+ (dequeue queue1)+ -- processor for the queue before the second work place+ let queueProcessor2 =+ queueProcessor+ (enqueue queue2)+ (dequeue queue2)+ -- the entire processor from input to output+ let entireProcessor =+ queueProcessor1 >>>+ processorParallel (map serverProcessor workplace1s) >>>+ -- foldr1 interposePrefetchProcessor (map serverProcessor workplace1s) >>>+ queueProcessor2 >>>+ processorParallel (map serverProcessor workplace2s) >>>+ -- foldr1 interposePrefetchProcessor (map serverProcessor workplace2s) >>>+ arrivalTimerProcessor arrivalTimer+ -- start simulating the model+ runProcessInStartTime $+ sinkStream $ runProcessor entireProcessor inputStream+ -- show the results in the final time+ runEventInStopTime $+ do queueSum1 <- queueSummary queue1 2+ queueSum2 <- queueSummary queue2 2+ workplaceSum1s <- forM workplace1s $ \x -> serverSummary x 2+ workplaceSum2s <- forM workplace2s $ \x -> serverSummary x 2+ processingTime <- arrivalProcessingTime arrivalTimer+ queueSize1 <- timingStatsAccumulated queueSizeAcc1+ queueSize2 <- timingStatsAccumulated queueSizeAcc2+ liftIO $+ do putStrLn ""+ putStrLn "--- the first queue summary (in the final time) ---"+ putStrLn ""+ putStrLn $ queueSum1 []+ putStrLn ""+ forM_ (zip [1..] workplaceSum1s) $ \(i, x) ->+ do putStrLn $ "--- the first work place no." ++ show i ++ " (in the final time) ---"+ putStrLn ""+ putStrLn $ x []+ putStrLn ""+ putStrLn "--- the second queue summary (in the final time) ---"+ putStrLn ""+ putStrLn $ queueSum2 []+ putStrLn ""+ forM_ (zip [1..] workplaceSum2s) $ \(i, x) ->+ do putStrLn $ "--- the second work place no. " ++ show i ++ " (in the final time) ---"+ putStrLn ""+ putStrLn $ x []+ putStrLn ""+ putStrLn "--- the processing time summary ---"+ putStrLn ""+ putStrLn $ samplingStatsSummary processingTime 2 []+ putStrLn ""+ putStrLn "--- the first queue size summary ---"+ putStrLn ""+ putStrLn $ timingStatsSummary queueSize1 2 []+ putStrLn ""+ putStrLn "--- the second queue size summary ---"+ putStrLn ""+ putStrLn $ timingStatsSummary queueSize2 2 []+ putStrLn ""++main = runSimulation model specs
examples/TimeOut.hs view
@@ -70,7 +70,7 @@ liftEvent $ do writeRef reactivatedCode 1 reactivateProcess nodePid- cancelProcessUsingId ackPid+ cancelProcessWithId ackPid acknowledge :: ProcessId -> Process () acknowledge timeoutPid =@@ -81,12 +81,12 @@ liftEvent $ do writeRef reactivatedCode 2 reactivateProcess nodePid- cancelProcessUsingId timeoutPid+ cancelProcessWithId timeoutPid - runProcessInStartTimeUsingId IncludingCurrentEvents+ runProcessInStartTimeUsingId nodePid node - runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do x <- readRef nTimeOuts y <- readRef nMsgs return $ x / y
examples/TimeOutInt.hs view
@@ -56,7 +56,7 @@ do interrupted <- processInterrupted nodePid if interrupted then modifyRef nTimeOuts $ (+) 1- else cancelProcessUsingId timeoutPid+ else cancelProcessWithId timeoutPid node timeout :: Process ()@@ -64,10 +64,10 @@ do holdProcess toPeriod liftEvent $ interruptProcess nodePid - runProcessInStartTimeUsingId IncludingCurrentEvents+ runProcessInStartTimeUsingId nodePid node - runEventInStopTime IncludingCurrentEvents $+ runEventInStopTime $ do x <- readRef nTimeOuts y <- readRef nMsgs return $ x / y
+ examples/TimeOutWait.hs view
@@ -0,0 +1,70 @@++-- It corresponds to model TimeOut described in document +-- Advanced Features of the SimPy Language+-- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/AdvancedSimPy.pdf]. +-- SimPy is available on [http://simpy.sourceforge.net/].+-- +-- The model description is as follows.+--+-- Introductory example to illustrate the modeling of "competing+-- events" such as timeouts, especially using the timeoutProcess+-- function. A network node starts a process within the specified +-- timeout and receives a signal that notifies whether the process +-- has finished successfully within the timeout; if the node+-- times out, it assumes the message it had sent was lost, and it+-- will send again. The time to get an acknowledgement for a message is+-- exponentially distributed with mean 1.0, and the timeout period is+-- 0.5. Immediately after receiving an acknowledgement, the node sends+-- out a new message.+--+-- We find the proportion of messages which timeout. The output should+-- be about 0.61.++import Control.Monad+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika++ackRate = 1.0 / 1.0 -- reciprocal of the acknowledge mean time+toPeriod = 0.5 -- timeout period++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 10000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Double+model =+ do -- number of messages sent+ nMsgs <- newRef 0+ + -- number of timeouts which have occured+ nTimeOuts <- newRef 0+ + let node :: Process ()+ node =+ do liftEvent $ modifyRef nMsgs $ (+) 1+ result <-+ timeoutProcess toPeriod $+ do ackTime <-+ liftParameter $+ randomExponential (1 / ackRate)+ holdProcess ackTime+ liftEvent $+ when (isNothing result) $+ modifyRef nTimeOuts $ (+) 1+ node++ runProcessInStartTime node+ + runEventInStopTime $+ do x <- readRef nTimeOuts+ y <- readRef nMsgs+ return $ x / y+ +main = + do putStr "The percentage of timeout was "+ runSimulation model specs >>= print
+ examples/WorkflowLoop.hs view
@@ -0,0 +1,191 @@++{-# LANGUAGE RecursiveDo, Arrows #-}++-- This is a model of the workflow with a loop. Also there are two infinite queues.+--+-- It is described in different sources [1, 2]. So, this is chapter 8 of [2].+--+-- [1] { add a foreign source in English }+--+-- [2] Труб И.И., Объектно-ориентированное моделирование на C++: Учебный курс. - СПб.: Питер, 2006++-- CAUTION: this is model is not yet fully tested and it may contain logical errors.++import Prelude hiding (id, (.)) ++import Control.Monad+import Control.Monad.Trans+import Control.Arrow+import Control.Category (id, (.))++import Simulation.Aivika+import Simulation.Aivika.Queue.Infinite++-- | The simulation specs.+specs = Specs { spcStartTime = 0.0,+ spcStopTime = 480.0,+ spcDT = 0.1,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }++-- the minimum delay for arriving the next TV set+minArrivalDelay = 3.5++-- the maximum delay for arriving the next TV set+maxArrivalDelay = 7.5++-- the minimum test time+minTestTime = 6++-- the maximum test time+maxTestTime = 12++-- the probability of passing the test+testPassingProb = 0.85++-- how many testers are there?+testerWorkplaceCount = 2++-- the minimum time of tuning the TV set +-- that has not passed the test+minTuningTime = 20++-- the maximum time of tuning the TV set+-- that has not passed the test+maxTuningTime = 40++-- how many persons perform a tuning of TV sets?+tunerWorkplaceCount = 1++-- create an accumulator to gather the queue size statistics +newQueueSizeAccumulator queue =+ newTimingStatsAccumulator $+ Signalable (queueCount queue) (queueCountChanged_ queue)++-- create a tester's workplace+newTesterWorkplace =+ newServer $ \a ->+ do holdProcess =<<+ (liftParameter $+ randomUniform minTestTime maxTestTime)+ passed <- + liftParameter $+ randomTrue testPassingProb+ if passed+ then return $ Right a+ else return $ Left a ++-- create a tuner's workplace+newTunerWorkplace =+ newServer $ \a ->+ do holdProcess =<<+ (liftParameter $+ randomUniform minTuningTime maxTuningTime)+ return a+ +model :: Simulation ()+model = mdo+ -- it will gather the statistics of the processing time+ inputArrivalTimer <- newArrivalTimer+ outputArrivalTimer <- newArrivalTimer+ -- define a stream of input events+ let inputStream =+ randomUniformStream minArrivalDelay maxArrivalDelay + -- create a queue before the tester's work place+ testerQueue <- newFCFSQueue+ -- create a queue before the tuner's work place+ tunerQueue <- newFCFSQueue+ -- the tester's queue size statistics+ testerQueueSizeAcc <- + runEventInStartTime $+ newQueueSizeAccumulator testerQueue+ -- the tuner's queue size statistics+ tunerQueueSizeAcc <- + runEventInStartTime $+ newQueueSizeAccumulator tunerQueue+ -- create the tester's work places, i.e. the "servers"+ testerWorkplaces <-+ forM [1 .. testerWorkplaceCount] $ \_ ->+ newTesterWorkplace+ -- create the tuner's work places, i.e. the "servers"+ tunerWorkplaces <-+ forM [1 .. tunerWorkplaceCount] $ \_ ->+ newTunerWorkplace+ -- a processor loop for the tester's queue+ let testerQueueProcessorLoop =+ queueProcessorLoopSeq+ (liftEvent . enqueue testerQueue)+ (dequeue testerQueue)+ testerProcessor+ (tunerQueueProcessor >>> tunerProcessor)+ -- a processor for the tuner's queue+ let tunerQueueProcessor =+ queueProcessor+ (liftEvent . enqueue tunerQueue)+ (dequeue tunerQueue)+ -- the parallel work of all the testers+ let testerProcessor =+ processorParallel (map serverProcessor testerWorkplaces)+ -- the parallel work of all the tuners+ let tunerProcessor =+ processorParallel (map serverProcessor tunerWorkplaces)+ -- the entire processor from input to output+ let entireProcessor =+ arrivalTimerProcessor inputArrivalTimer >>>+ testerQueueProcessorLoop >>>+ arrivalTimerProcessor outputArrivalTimer+ -- start simulating the model+ runProcessInStartTime $+ sinkStream $ runProcessor entireProcessor inputStream+ -- show the results in the final time+ runEventInStopTime $+ do testerQueueSum <- queueSummary testerQueue 2+ tunerQueueSum <- queueSummary tunerQueue 2+ testerWorkplaceSums <- + forM testerWorkplaces $ \x -> serverSummary x 2+ tunerWorkplaceSums <- + forM tunerWorkplaces $ \x -> serverSummary x 2+ inputProcessingTime <- arrivalProcessingTime inputArrivalTimer+ outputProcessingTime <- arrivalProcessingTime outputArrivalTimer+ testerQueueSize <- timingStatsAccumulated testerQueueSizeAcc+ tunerQueueSize <- timingStatsAccumulated tunerQueueSizeAcc+ liftIO $+ do putStrLn ""+ putStrLn "--- the tester's queue summary (in the final time) ---"+ putStrLn ""+ putStrLn $ testerQueueSum []+ putStrLn ""+ forM_ (zip [1..] testerWorkplaceSums) $ \(i, x) ->+ do putStrLn $ "--- the tester's work place no."+ ++ show i ++ " (in the final time) ---"+ putStrLn ""+ putStrLn $ x []+ putStrLn ""+ putStrLn "--- the tuner's queue summary (in the final time) ---"+ putStrLn ""+ putStrLn $ tunerQueueSum []+ putStrLn ""+ forM_ (zip [1..] tunerWorkplaceSums) $ \(i, x) ->+ do putStrLn $ "--- the tuner's work place no. "+ ++ show i ++ " (in the final time) ---"+ putStrLn ""+ putStrLn $ x []+ putStrLn ""+ putStrLn "--- the arrival receiving time summary (we are interested in their count) ---"+ putStrLn ""+ putStrLn $ samplingStatsSummary inputProcessingTime 2 []+ putStrLn ""+ putStrLn "--- the arrival processing time summary ---"+ putStrLn ""+ putStrLn $ samplingStatsSummary outputProcessingTime 2 []+ putStrLn ""+ putStrLn "--- the tester's queue size summary (updated when enqueueing and dequeueing) ---"+ putStrLn ""+ putStrLn $ timingStatsSummary testerQueueSize 2 []+ putStrLn ""+ putStrLn "--- the tuner's queue size summary (updated when enqueueing and dequeueing) ---"+ putStrLn ""+ putStrLn $ timingStatsSummary tunerQueueSize 2 []+ putStrLn ""++main = runSimulation model specs