aivika 4.6 → 5.0.1
raw patch · 10 files changed
+609/−40 lines, 10 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Simulation.Aivika.Processor: processorSignaling :: Processor a b -> Signal a -> Process (Signal b)
- Simulation.Aivika.Processor: signalProcessor :: (Signal a -> Signal b) -> Processor a b
+ Simulation.Aivika.Arrival: arrivalTimerChannel :: ArrivalTimer -> Channel (Arrival a) (Arrival a)
+ Simulation.Aivika.Arrival: arrivalTimerSignal :: ArrivalTimer -> Signal (Arrival a) -> Signal (Arrival a)
+ Simulation.Aivika.Channel: Channel :: (Signal a -> Composite (Signal b)) -> Channel a b
+ Simulation.Aivika.Channel: [runChannel] :: Channel a b -> Signal a -> Composite (Signal b)
+ Simulation.Aivika.Channel: delayChannel :: Double -> Channel a a
+ Simulation.Aivika.Channel: delayChannelM :: Event Double -> Channel a a
+ Simulation.Aivika.Channel: instance Control.Category.Category Simulation.Aivika.Channel.Channel
+ Simulation.Aivika.Channel: newtype Channel a b
+ Simulation.Aivika.Channel: sinkSignal :: Signal a -> Composite ()
+ Simulation.Aivika.Channel: traceChannel :: String -> Channel a b -> Channel a b
+ Simulation.Aivika.Composite: data Composite a
+ Simulation.Aivika.Composite: disposableComposite :: DisposableEvent -> Composite ()
+ Simulation.Aivika.Composite: instance Control.Monad.Fix.MonadFix Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance Control.Monad.IO.Class.MonadIO Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance GHC.Base.Applicative Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance GHC.Base.Functor Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance GHC.Base.Monad Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance Simulation.Aivika.Internal.Dynamics.DynamicsLift Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance Simulation.Aivika.Internal.Event.EventLift Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance Simulation.Aivika.Internal.Parameter.ParameterLift Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: instance Simulation.Aivika.Internal.Simulation.SimulationLift Simulation.Aivika.Composite.Composite
+ Simulation.Aivika.Composite: runComposite :: Composite a -> DisposableEvent -> Event (a, DisposableEvent)
+ Simulation.Aivika.Composite: runCompositeInStartTime_ :: Composite a -> Simulation a
+ Simulation.Aivika.Composite: runCompositeInStopTime_ :: Composite a -> Simulation a
+ Simulation.Aivika.Composite: runComposite_ :: Composite a -> Event a
+ Simulation.Aivika.Processor: channelProcessor :: Channel a b -> Processor a b
+ Simulation.Aivika.Processor: processorChannel :: Processor a b -> Channel a b
+ Simulation.Aivika.Processor: queuedChannelProcessor :: (b -> Event ()) -> Process b -> Channel a b -> Processor a b
+ Simulation.Aivika.Processor: queuedProcessorChannel :: (a -> Event ()) -> (Process a) -> Processor a b -> Channel a b
+ Simulation.Aivika.Signal.Random: newRandomBetaSignal :: Double -> Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomBinomialSignal :: Double -> Int -> Composite (Signal (Arrival Int))
+ Simulation.Aivika.Signal.Random: newRandomDiscreteSignal :: DiscretePDF Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomErlangSignal :: Double -> Int -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomExponentialSignal :: Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomGammaSignal :: Double -> Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomLogNormalSignal :: Double -> Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomNormalSignal :: Double -> Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomPoissonSignal :: Double -> Composite (Signal (Arrival Int))
+ Simulation.Aivika.Signal.Random: newRandomSignal :: Parameter (Double, a) -> Composite (Signal (Arrival a))
+ Simulation.Aivika.Signal.Random: newRandomTriangularSignal :: Double -> Double -> Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomUniformIntSignal :: Int -> Int -> Composite (Signal (Arrival Int))
+ Simulation.Aivika.Signal.Random: newRandomUniformSignal :: Double -> Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Signal.Random: newRandomWeibullSignal :: Double -> Double -> Composite (Signal (Arrival Double))
+ Simulation.Aivika.Stream: queuedSignalStream :: (a -> Event ()) -> Process a -> Signal a -> Composite (Stream a)
- Simulation.Aivika.Stream: signalStream :: Signal a -> Process (Stream a)
+ Simulation.Aivika.Stream: signalStream :: Signal a -> Composite (Stream a)
- Simulation.Aivika.Stream: streamSignal :: Stream a -> Process (Signal a)
+ Simulation.Aivika.Stream: streamSignal :: Stream a -> Composite (Signal a)
Files
- CHANGELOG.md +19/−0
- Simulation/Aivika.hs +6/−0
- Simulation/Aivika/Arrival.hs +27/−0
- Simulation/Aivika/Channel.hs +80/−0
- Simulation/Aivika/Composite.hs +127/−0
- Simulation/Aivika/Processor.hs +72/−16
- Simulation/Aivika/Signal/Random.hs +235/−0
- Simulation/Aivika/Stream.hs +37/−22
- Simulation/Aivika/Stream/Random.hs +2/−1
- aivika.cabal +4/−1
CHANGELOG.md view
@@ -1,4 +1,23 @@ +Version 5.0+-----++* Added the Composite monad.++* Added the Channel computation.++* Breaking change: modified signatures of functions signalStream and streamSignal.++* Breaking change: the signalProcessor function is replaced with channelProcessor.++* Breaking change: the processorSignaling function is replaced with processorChannel.++* Added module Signal.Random.++* Added functions arrivalTimerSignal and arrivalTimerChannel.++* Added functions queuedSignalStream, queuedProcessorChannel and queuedChannelProcessor.+ Version 4.6 -----
Simulation/Aivika.hs view
@@ -16,7 +16,9 @@ module Simulation.Aivika.Activity.Random, module Simulation.Aivika.Agent, module Simulation.Aivika.Arrival,+ module Simulation.Aivika.Channel, module Simulation.Aivika.Circuit,+ module Simulation.Aivika.Composite, module Simulation.Aivika.Cont, module Simulation.Aivika.Dynamics, module Simulation.Aivika.Dynamics.Extra,@@ -45,6 +47,7 @@ module Simulation.Aivika.Server, module Simulation.Aivika.Server.Random, module Simulation.Aivika.Signal,+ module Simulation.Aivika.Signal.Random, module Simulation.Aivika.Simulation, module Simulation.Aivika.Specs, module Simulation.Aivika.Statistics,@@ -61,7 +64,9 @@ import Simulation.Aivika.Activity.Random import Simulation.Aivika.Agent import Simulation.Aivika.Arrival+import Simulation.Aivika.Channel import Simulation.Aivika.Circuit+import Simulation.Aivika.Composite import Simulation.Aivika.Cont import Simulation.Aivika.Dynamics import Simulation.Aivika.Dynamics.Extra@@ -90,6 +95,7 @@ import Simulation.Aivika.Server import Simulation.Aivika.Server.Random import Simulation.Aivika.Signal+import Simulation.Aivika.Signal.Random import Simulation.Aivika.Simulation import Simulation.Aivika.Specs import Simulation.Aivika.Statistics
Simulation/Aivika/Arrival.hs view
@@ -18,6 +18,8 @@ ArrivalTimer, newArrivalTimer, arrivalTimerProcessor,+ arrivalTimerSignal,+ arrivalTimerChannel, arrivalProcessingTime, arrivalProcessingTimeChanged, arrivalProcessingTimeChanged_) where@@ -28,11 +30,13 @@ import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics import Simulation.Aivika.Event+import Simulation.Aivika.Composite import Simulation.Aivika.Processor import Simulation.Aivika.Stream import Simulation.Aivika.Statistics import Simulation.Aivika.Ref import Simulation.Aivika.Signal+import Simulation.Aivika.Channel import Simulation.Aivika.Internal.Arrival -- | Accumulates the statistics about that how long the arrived events are processed.@@ -75,3 +79,26 @@ addSamplingStats (t - arrivalTime a) triggerSignal (arrivalProcessingTimeChangedSource timer) () return (a, Cons $ loop xs)++-- | Return a signal that actually measures how much time has passed from+-- the time of arriving the events.+--+-- Note that the statistics is counted each time you subscribe to the output signal.+-- For example, if you subscribe twice then the statistics counting is duplicated.+-- Ideally, you should subscribe to the output signal only once.+arrivalTimerSignal :: ArrivalTimer -> Signal (Arrival a) -> Signal (Arrival a)+arrivalTimerSignal timer sa =+ Signal { handleSignal = \h ->+ handleSignal sa $ \a ->+ do t <- liftDynamics time+ modifyRef (arrivalProcessingTimeRef timer) $+ addSamplingStats (t - arrivalTime a)+ h a+ }++-- | Like 'arrivalTimerSignal' but measures how much time has passed from+-- the time of arriving the events in the channel.+arrivalTimerChannel :: ArrivalTimer -> Channel (Arrival a) (Arrival a)+arrivalTimerChannel timer =+ Channel $ \sa ->+ return $ arrivalTimerSignal timer sa
+ Simulation/Aivika/Channel.hs view
@@ -0,0 +1,80 @@++-- |+-- Module : Simulation.Aivika.Channel+-- Copyright : Copyright (c) 2009-2016, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.1+--+-- The module defines a channel that transforms one 'Signal' to another+-- within the 'Composite' computation.+--+module Simulation.Aivika.Channel+ (-- * Channel Computation+ Channel(..),+ -- * Delay Channel+ delayChannel,+ delayChannelM,+ -- * Sinking Signal+ sinkSignal,+ -- * Debugging+ traceChannel) where++import qualified Control.Category as C+import Control.Monad++import Simulation.Aivika.Simulation+import Simulation.Aivika.Dynamics+import Simulation.Aivika.Event+import Simulation.Aivika.Signal+import Simulation.Aivika.Composite++-- | It allows representing a signal transformation.+newtype Channel a b =+ Channel { runChannel :: Signal a -> Composite (Signal b)+ -- ^ Run the channel transform.+ }++instance C.Category Channel where++ id = Channel return+ + (Channel g) . (Channel f) =+ Channel $ \a -> f a >>= g++-- | Return a delayed signal.+--+-- This is actually the 'delaySignal' function wrapped in the 'Channel' type. +delayChannel :: Double -- ^ the delay+ -> Channel a a -- ^ the delay channel+delayChannel delay =+ Channel $ \a -> return $ delaySignal delay a++-- | Like 'delayChannel', but it re-computes the delay each time.+--+-- This is actually the 'delaySignalM' function wrapped in the 'Channel' type. +delayChannelM :: Event Double -- ^ the delay+ -> Channel a a -- ^ the delay channel+delayChannelM delay =+ Channel $ \a -> return $ delaySignalM delay a++-- | Sink the signal. It returns a computation that subscribes to+-- the signal and then ignores the received data. The resulting+-- computation can be a moving force to simulate the whole system of+-- the interconnected signals and channels.+sinkSignal :: Signal a -> Composite ()+sinkSignal a =+ do h <- liftEvent $+ handleSignal a $+ const $ return ()+ disposableComposite h+ +-- | Show the debug message with the current simulation time,+-- when emitting the output signal.+traceChannel :: String -> Channel a b -> Channel a b+traceChannel message (Channel f) =+ Channel $ \a ->+ do b <- f a+ return $+ traceSignal message b
+ Simulation/Aivika/Composite.hs view
@@ -0,0 +1,127 @@++{-# LANGUAGE MultiParamTypeClasses, RecursiveDo #-}++-- |+-- Module : Simulation.Aivika.Composite+-- Copyright : Copyright (c) 2009-2016, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.1+--+-- It defines the 'Composite' monad that allows constructing components which+-- can be then destroyed in case of need.+--+module Simulation.Aivika.Composite+ (-- * Composite Monad+ Composite,+ runComposite,+ runComposite_,+ runCompositeInStartTime_,+ runCompositeInStopTime_,+ disposableComposite) where++import Data.Monoid++import Control.Exception+import Control.Monad+import Control.Monad.Trans+import Control.Monad.Fix+import Control.Applicative++import Simulation.Aivika.Parameter+import Simulation.Aivika.Simulation+import Simulation.Aivika.Dynamics+import Simulation.Aivika.Event++-- | It represents a composite which can be then destroyed in case of need.+newtype Composite a = Composite { runComposite :: DisposableEvent -> Event (a, DisposableEvent)+ -- ^ Run the computation returning the result+ -- and some 'DisposableEvent' that being applied+ -- destroys the composite, for example, unsubscribes+ -- from signals or cancels the processes.+ --+ }++-- | Like 'runComposite' but retains the composite parts during the simulation.+runComposite_ :: Composite a -> Event a+runComposite_ m =+ do (a, _) <- runComposite m mempty+ return a++-- | Like 'runComposite_' but runs the computation in the start time.+runCompositeInStartTime_ :: Composite a -> Simulation a+runCompositeInStartTime_ = runEventInStartTime . runComposite_++-- | Like 'runComposite_' but runs the computation in the stop time.+runCompositeInStopTime_ :: Composite a -> Simulation a+runCompositeInStopTime_ = runEventInStopTime . runComposite_++-- | When destroying the composite, the specified action will be applied.+disposableComposite :: DisposableEvent -> Composite ()+disposableComposite h = Composite $ \h0 -> return ((), h0 <> h)++instance Functor Composite where++ fmap f (Composite m) =+ Composite $ \h0 ->+ do (a, h) <- m h0+ return (f a, h)++instance Applicative Composite where++ pure = return+ (<*>) = ap++instance Monad Composite where++ return a = Composite $ \h0 -> return (a, h0)++ (Composite m) >>= k =+ Composite $ \h0 ->+ do (a, h) <- m h0+ let Composite m' = k a+ (b, h') <- m' h+ return (b, h')++instance MonadIO Composite where++ liftIO m =+ Composite $ \h0 ->+ do a <- liftIO m+ return (a, h0)++instance MonadFix Composite where++ mfix f =+ Composite $ \h0 ->+ do rec (a, h) <- runComposite (f a) h0+ return (a, h)++instance ParameterLift Composite where++ liftParameter m =+ Composite $ \h0 ->+ do a <- liftParameter m+ return (a, h0)++instance SimulationLift Composite where++ liftSimulation m =+ Composite $ \h0 ->+ do a <- liftSimulation m+ return (a, h0)++instance DynamicsLift Composite where++ liftDynamics m =+ Composite $ \h0 ->+ do a <- liftDynamics m+ return (a, h0)++instance EventLift Composite where++ liftEvent m =+ Composite $ \h0 ->+ do a <- liftEvent m+ return (a, h0)
Simulation/Aivika/Processor.hs view
@@ -44,23 +44,29 @@ joinProcessor, -- * Failover failoverProcessor,- -- * Integrating with Signals- signalProcessor,- processorSignaling,+ -- * Integrating with Signals and Channels+ channelProcessor,+ processorChannel,+ queuedChannelProcessor,+ queuedProcessorChannel, -- * Debugging traceProcessor) where import qualified Control.Category as C import Control.Arrow +import Data.Monoid+ import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics import Simulation.Aivika.Event+import Simulation.Aivika.Composite import Simulation.Aivika.Cont import Simulation.Aivika.Process import Simulation.Aivika.Stream import Simulation.Aivika.QueueStrategy import Simulation.Aivika.Signal+import Simulation.Aivika.Channel import Simulation.Aivika.Internal.Arrival -- | Represents a processor of simulation data.@@ -424,39 +430,89 @@ prefetchProcessor :: Processor a a prefetchProcessor = Processor prefetchStream --- | Convert the specified signal transform to a processor.+-- | Convert the specified signal transform, i.e. the channel, to a processor. -- -- The processor may return data with delay as the values are requested by demand. -- Consider using the 'arrivalSignal' function to provide with the information -- about the time points at which the signal was actually triggered. -- -- The point is that the 'Stream' used in the 'Processor' is requested outside, --- while the 'Signal' is triggered inside. They are different by nature. +-- while the 'Signal' used in the 'Channel' is triggered inside. They are different by nature. -- The former is passive, while the latter is active. ----- Cancel the processor's process to unsubscribe from the signals provided.-signalProcessor :: (Signal a -> Signal b) -> Processor a b-signalProcessor f =+-- The resulting processor may be a root of space leak as it uses an internal queue to store+-- the values received from the input signal. Consider using 'queuedChannelProcessor' that+-- allows specifying the bounded queue in case of need.+channelProcessor :: Channel a b -> Processor a b+channelProcessor f = Processor $ \xs -> Cons $- do sa <- streamSignal xs- sb <- signalStream (f sa)- runStream sb+ do let composite =+ do sa <- streamSignal xs+ sb <- runChannel f sa+ signalStream sb+ (ys, h) <- liftEvent $+ runComposite composite mempty+ whenCancellingProcess $+ disposeEvent h+ runStream ys --- | Convert the specified processor to a signal transform. +-- | Convert the specified processor to a signal transform, i.e. the channel. -- -- The processor may return data with delay as the values are requested by demand. -- Consider using the 'arrivalSignal' function to provide with the information -- about the time points at which the signal was actually triggered. -- -- The point is that the 'Stream' used in the 'Processor' is requested outside, --- while the 'Signal' is triggered inside. They are different by nature.+-- while the 'Signal' used in the 'Channel' is triggered inside. They are different by nature. -- The former is passive, while the latter is active. ----- Cancel the returned process to unsubscribe from the signal specified.-processorSignaling :: Processor a b -> Signal a -> Process (Signal b)-processorSignaling (Processor f) sa =+-- The resulting channel may be a root of space leak as it uses an internal queue to store+-- the values received from the input stream. Consider using 'queuedProcessorChannel' that+-- allows specifying the bounded queue in case of need.+processorChannel :: Processor a b -> Channel a b+processorChannel (Processor f) =+ Channel $ \sa -> do xs <- signalStream sa+ let ys = f xs+ streamSignal ys++-- | Like 'channelProcessor' but allows specifying an arbitrary queue for storing the signal values,+-- for example, the bounded queue.+queuedChannelProcessor :: (b -> Event ())+ -- ^ enqueue+ -> Process b+ -- ^ dequeue+ -> Channel a b+ -- ^ the channel+ -> Processor a b+ -- ^ the processor+queuedChannelProcessor enqueue dequeue f =+ Processor $ \xs ->+ Cons $+ do let composite =+ do sa <- streamSignal xs+ sb <- runChannel f sa+ queuedSignalStream enqueue dequeue sb+ (ys, h) <- liftEvent $+ runComposite composite mempty+ whenCancellingProcess $+ disposeEvent h+ runStream ys++-- | Like 'processorChannel' but allows specifying an arbitrary queue for storing the signal values,+-- for example, the bounded queue.+queuedProcessorChannel :: (a -> Event ())+ -- ^ enqueue+ -> (Process a)+ -- ^ dequeue+ -> Processor a b+ -- ^ the processor+ -> Channel a b+ -- ^ the channel+queuedProcessorChannel enqueue dequeue (Processor f) =+ Channel $ \sa ->+ do xs <- queuedSignalStream enqueue dequeue sa let ys = f xs streamSignal ys
+ Simulation/Aivika/Signal/Random.hs view
@@ -0,0 +1,235 @@++-- |+-- Module : Simulation.Aivika.Signal.Random+-- Copyright : Copyright (c) 2009-2016, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.1+--+-- This module defines random signals of events, which are useful+-- for describing the input of the model.+--++module Simulation.Aivika.Signal.Random+ (-- * Signal of Random Events+ newRandomSignal,+ newRandomUniformSignal,+ newRandomUniformIntSignal,+ newRandomTriangularSignal,+ newRandomNormalSignal,+ newRandomLogNormalSignal,+ newRandomExponentialSignal,+ newRandomErlangSignal,+ newRandomPoissonSignal,+ newRandomBinomialSignal,+ newRandomGammaSignal,+ newRandomBetaSignal,+ newRandomWeibullSignal,+ newRandomDiscreteSignal) where++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Generator+import Simulation.Aivika.Parameter+import Simulation.Aivika.Parameter.Random+import Simulation.Aivika.Simulation+import Simulation.Aivika.Dynamics+import Simulation.Aivika.Event+import Simulation.Aivika.Composite+import Simulation.Aivika.Process+import Simulation.Aivika.Signal+import Simulation.Aivika.Statistics+import Simulation.Aivika.Arrival++-- | Return a signal of random events that arrive with the specified delay.+newRandomSignal :: Parameter (Double, a)+ -- ^ compute a pair of the delay and event of type @a@+ -> Composite (Signal (Arrival a))+ -- ^ the computation that returns a signal emitting the delayed events+newRandomSignal delay =+ do source <- liftSimulation newSignalSource+ let loop t0 =+ do (delay, a) <- liftParameter delay+ when (delay > 0) $+ holdProcess delay+ t2 <- liftDynamics time+ let arrival = Arrival { arrivalValue = a,+ arrivalTime = t2,+ arrivalDelay =+ case t0 of+ Nothing -> Nothing+ Just t0 -> Just delay }+ liftEvent $+ triggerSignal source arrival+ loop (Just t2)+ pid <- liftSimulation newProcessId+ liftEvent $+ runProcessUsingId pid $+ loop Nothing+ disposableComposite $+ DisposableEvent $+ cancelProcessWithId pid+ return $ publishSignal source++-- | Create a new signal with random delays distributed uniformly.+newRandomUniformSignal :: Double+ -- ^ the minimum delay+ -> Double+ -- ^ the maximum delay+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomUniformSignal min max =+ newRandomSignal $+ randomUniform min max >>= \x ->+ return (x, x)++-- | Create a new signal with integer random delays distributed uniformly.+newRandomUniformIntSignal :: Int+ -- ^ the minimum delay+ -> Int+ -- ^ the maximum delay+ -> Composite (Signal (Arrival Int))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomUniformIntSignal min max =+ newRandomSignal $+ randomUniformInt min max >>= \x ->+ return (fromIntegral x, x)++-- | Create a new signal with random delays having the triangular distribution.+newRandomTriangularSignal :: Double+ -- ^ the minimum delay+ -> Double+ -- ^ the median of the delay+ -> Double+ -- ^ the maximum delay+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomTriangularSignal min median max =+ newRandomSignal $+ randomTriangular min median max >>= \x ->+ return (x, x)++-- | Create a new signal with random delays distributed normally.+newRandomNormalSignal :: Double+ -- ^ the mean delay+ -> Double+ -- ^ the delay deviation+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomNormalSignal mu nu =+ newRandomSignal $+ randomNormal mu nu >>= \x ->+ return (x, x)++-- | Create a new signal with random delays having the lognormal distribution.+newRandomLogNormalSignal :: Double+ -- ^ the mean of a normal distribution which+ -- this distribution is derived from+ -> Double+ -- ^ the deviation of a normal distribution which+ -- this distribution is derived from+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomLogNormalSignal mu nu =+ newRandomSignal $+ randomLogNormal mu nu >>= \x ->+ return (x, x)++-- | Return a new signal with random delays distibuted exponentially with the specified mean+-- (the reciprocal of the rate).+newRandomExponentialSignal :: Double+ -- ^ the mean delay (the reciprocal of the rate)+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomExponentialSignal mu =+ newRandomSignal $+ randomExponential mu >>= \x ->+ return (x, x)+ +-- | Return a new signal with random delays having the Erlang distribution with the specified+-- scale (the reciprocal of the rate) and shape parameters.+newRandomErlangSignal :: Double+ -- ^ the scale (the reciprocal of the rate)+ -> Int+ -- ^ the shape+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomErlangSignal beta m =+ newRandomSignal $+ randomErlang beta m >>= \x ->+ return (x, x)++-- | Return a new signal with random delays having the Poisson distribution with+-- the specified mean.+newRandomPoissonSignal :: Double+ -- ^ the mean delay+ -> Composite (Signal (Arrival Int))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomPoissonSignal mu =+ newRandomSignal $+ randomPoisson mu >>= \x ->+ return (fromIntegral x, x)++-- | Return a new signal with random delays having the binomial distribution with the specified+-- probability and trials.+newRandomBinomialSignal :: Double+ -- ^ the probability+ -> Int+ -- ^ the number of trials+ -> Composite (Signal (Arrival Int))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomBinomialSignal prob trials =+ newRandomSignal $+ randomBinomial prob trials >>= \x ->+ return (fromIntegral x, x)++-- | Return a new signal with random delays having the Gamma distribution by the specified+-- shape and scale.+newRandomGammaSignal :: Double+ -- ^ the shape+ -> Double+ -- ^ the scale (a reciprocal of the rate)+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomGammaSignal kappa theta =+ newRandomSignal $+ randomGamma kappa theta >>= \x ->+ return (x, x)++-- | Return a new signal with random delays having the Beta distribution by the specified+-- shape parameters (alpha and beta).+newRandomBetaSignal :: Double+ -- ^ the shape (alpha)+ -> Double+ -- ^ the shape (beta)+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomBetaSignal alpha beta =+ newRandomSignal $+ randomBeta alpha beta >>= \x ->+ return (x, x)++-- | Return a new signal with random delays having the Weibull distribution by the specified+-- shape and scale.+newRandomWeibullSignal :: Double+ -- ^ shape+ -> Double+ -- ^ scale+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomWeibullSignal alpha beta =+ newRandomSignal $+ randomWeibull alpha beta >>= \x ->+ return (x, x)++-- | Return a new signal with random delays having the specified discrete distribution.+newRandomDiscreteSignal :: DiscretePDF Double+ -- ^ the discrete probability density function+ -> Composite (Signal (Arrival Double))+ -- ^ the computation of signal emitting random events with the delays generated+newRandomDiscreteSignal dpdf =+ newRandomSignal $+ randomDiscrete dpdf >>= \x ->+ return (x, x)
Simulation/Aivika/Stream.hs view
@@ -66,6 +66,7 @@ -- * Integrating with Signals signalStream, streamSignal,+ queuedSignalStream, -- * Utilities leftStream, rightStream,@@ -91,12 +92,13 @@ import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics import Simulation.Aivika.Event+import Simulation.Aivika.Composite import Simulation.Aivika.Cont import Simulation.Aivika.Process import Simulation.Aivika.Signal import Simulation.Aivika.Resource.Base import Simulation.Aivika.QueueStrategy-import Simulation.Aivika.Queue.Infinite.Base+import qualified Simulation.Aivika.Queue.Infinite.Base as IQ import Simulation.Aivika.Internal.Arrival -- | Represents an infinite stream of data in time,@@ -551,6 +553,21 @@ spawnProcess $ writer s runStream $ repeatProcess reader +-- | Like 'signalStream' but allows specifying an arbitrary queue instead of the unbounded queue.+queuedSignalStream :: (a -> Event ())+ -- ^ enqueue+ -> Process a+ -- ^ dequeue+ -> Signal a+ -- ^ the input signal+ -> Composite (Stream a)+ -- ^ the output stream+queuedSignalStream enqueue dequeue s =+ do h <- liftEvent $+ handleSignal s enqueue+ disposableComposite h+ return $ repeatProcess dequeue+ -- | Return a stream of values triggered by the specified signal. -- -- Since the time at which the values of the stream are requested for may differ from@@ -561,32 +578,30 @@ -- The point is that the 'Stream' is requested outside, while the 'Signal' is triggered -- inside. They are different by nature. The former is passive, while the latter is active. ----- The resulting stream may be a root of space leak as it uses an internal queue to store+-- The resulting stream may be a root of space leak as it uses an internal unbounded queue to store -- the values received from the signal. The oldest value is dequeued each time we request--- the stream and it is returned within the computation.------ Cancel the stream's process to unsubscribe from the specified signal.-signalStream :: Signal a -> Process (Stream a)+-- the stream and it is returned within the computation. Consider using 'queuedSignalStream' that+-- allows specifying the bounded queue in case of need.+signalStream :: Signal a -> Composite (Stream a) signalStream s =- do q <- liftSimulation newFCFSQueue- h <- liftEvent $- handleSignal s $ - enqueue q- whenCancellingProcess $ disposeEvent h- return $ repeatProcess $ dequeue q+ do q <- liftSimulation IQ.newFCFSQueue+ queuedSignalStream (IQ.enqueue q) (IQ.dequeue q) s --- | Return a computation of the signal that triggers values from the specified stream,+-- | Return a computation of the disposable signal that triggers values from the specified stream, -- each time the next value of the stream is received within the underlying 'Process' -- computation.------ Cancel the returned process to stop reading from the specified stream. -streamSignal :: Stream a -> Process (Signal a)+streamSignal :: Stream a -> Composite (Signal a) streamSignal z = do s <- liftSimulation newSignalSource- spawnProcess $+ pid <- liftSimulation newProcessId+ liftEvent $+ runProcessUsingId pid $ consumeStream (liftEvent . triggerSignal s) z+ disposableComposite $+ DisposableEvent $+ cancelProcessWithId pid return $ publishSignal s-+ -- | Transform a stream so that the resulting stream returns a sequence of arrivals -- saving the information about the time points at which the original stream items -- were received by demand.@@ -716,16 +731,16 @@ -- | Create the specified number of equivalent clones of the input stream. cloneStream :: Int -> Stream a -> Simulation [Stream a] cloneStream n s =- do qs <- forM [1..n] $ \i -> newFCFSQueue+ do qs <- forM [1..n] $ \i -> IQ.newFCFSQueue rs <- newFCFSResource 1 ref <- liftIO $ newIORef s let reader m q =- do a <- liftEvent $ tryDequeue q+ do a <- liftEvent $ IQ.tryDequeue q case a of Just a -> return a Nothing -> usingResource rs $- do a <- liftEvent $ tryDequeue q+ do a <- liftEvent $ IQ.tryDequeue q case a of Just a -> return a Nothing ->@@ -734,7 +749,7 @@ liftIO $ writeIORef ref xs forM_ (zip [1..] qs) $ \(i, q) -> unless (i == m) $- liftEvent $ enqueue q a+ liftEvent $ IQ.enqueue q a return a forM (zip [1..] qs) $ \(i, q) -> return $ repeatProcess $ reader i q
Simulation/Aivika/Stream/Random.hs view
@@ -63,7 +63,8 @@ "At least, they can be lost, for example, when trying to enqueue them, but " ++ "the random stream itself must always work: randomStream." (delay, a) <- liftParameter delay- holdProcess delay+ when (delay > 0) $+ holdProcess delay t2 <- liftDynamics time let arrival = Arrival { arrivalValue = a, arrivalTime = t2,
aivika.cabal view
@@ -1,5 +1,5 @@ name: aivika-version: 4.6+version: 5.0.1 synopsis: A multi-method simulation library description: Aivika is a multi-method simulation library focused on @@ -148,7 +148,9 @@ Simulation.Aivika.Activity.Random Simulation.Aivika.Agent Simulation.Aivika.Arrival+ Simulation.Aivika.Channel Simulation.Aivika.Circuit+ Simulation.Aivika.Composite Simulation.Aivika.Cont Simulation.Aivika.DoubleLinkedList Simulation.Aivika.Dynamics@@ -190,6 +192,7 @@ Simulation.Aivika.Server Simulation.Aivika.Server.Random Simulation.Aivika.Signal+ Simulation.Aivika.Signal.Random Simulation.Aivika.Simulation Simulation.Aivika.Specs Simulation.Aivika.Statistics