aivika 0.6.1 → 0.7
raw patch · 74 files changed
+5433/−5593 lines, 74 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Simulation.Aivika.Dynamics.Agent: activateState :: AgentState -> Dynamics ()
- Simulation.Aivika.Dynamics.Agent: addTimeout :: AgentState -> Double -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.Agent: addTimer :: AgentState -> Dynamics Double -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.Agent: agentQueue :: Agent -> EventQueue
- Simulation.Aivika.Dynamics.Agent: agentState :: Agent -> Dynamics (Maybe AgentState)
- Simulation.Aivika.Dynamics.Agent: agentStateChanged :: Agent -> Signal (Maybe AgentState)
- Simulation.Aivika.Dynamics.Agent: agentStateChanged_ :: Agent -> Signal ()
- Simulation.Aivika.Dynamics.Agent: data Agent
- Simulation.Aivika.Dynamics.Agent: data AgentState
- Simulation.Aivika.Dynamics.Agent: initState :: AgentState -> Dynamics ()
- Simulation.Aivika.Dynamics.Agent: instance Eq Agent
- Simulation.Aivika.Dynamics.Agent: instance Eq AgentState
- Simulation.Aivika.Dynamics.Agent: newAgent :: EventQueue -> Simulation Agent
- Simulation.Aivika.Dynamics.Agent: newState :: Agent -> Simulation AgentState
- Simulation.Aivika.Dynamics.Agent: newSubstate :: AgentState -> Simulation AgentState
- Simulation.Aivika.Dynamics.Agent: setStateActivation :: AgentState -> Dynamics () -> Simulation ()
- Simulation.Aivika.Dynamics.Agent: setStateDeactivation :: AgentState -> Dynamics () -> Simulation ()
- Simulation.Aivika.Dynamics.Agent: setStateTransition :: AgentState -> Dynamics (Maybe AgentState) -> Simulation ()
- Simulation.Aivika.Dynamics.Agent: stateActivation :: AgentState -> Dynamics () -> Simulation ()
- Simulation.Aivika.Dynamics.Agent: stateAgent :: AgentState -> Agent
- Simulation.Aivika.Dynamics.Agent: stateDeactivation :: AgentState -> Dynamics () -> Simulation ()
- Simulation.Aivika.Dynamics.Agent: stateParent :: AgentState -> Maybe AgentState
- Simulation.Aivika.Dynamics.Base: discrete :: Dynamics a -> Dynamics a
- Simulation.Aivika.Dynamics.Base: divideDynamics :: Dynamics Double -> Dynamics Double
- Simulation.Aivika.Dynamics.Base: dt :: Dynamics Double
- Simulation.Aivika.Dynamics.Base: foldDynamics :: (Dynamics a -> Simulation (Dynamics a)) -> (a -> b -> a) -> a -> Dynamics b -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.Base: foldDynamics1 :: (Dynamics a -> Simulation (Dynamics a)) -> (a -> a -> a) -> Dynamics a -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.Base: initDynamics :: Dynamics a -> Dynamics a
- Simulation.Aivika.Dynamics.Base: integIteration :: Dynamics Int
- Simulation.Aivika.Dynamics.Base: integIterationBnds :: Specs -> (Int, Int)
- Simulation.Aivika.Dynamics.Base: integIterationHiBnd :: Specs -> Int
- Simulation.Aivika.Dynamics.Base: integIterationLoBnd :: Specs -> Int
- Simulation.Aivika.Dynamics.Base: integTimes :: Specs -> [Double]
- Simulation.Aivika.Dynamics.Base: interpolate :: Dynamics a -> Dynamics a
- Simulation.Aivika.Dynamics.Base: isTimeInteg :: Dynamics Bool
- Simulation.Aivika.Dynamics.Base: iterateDynamics :: Dynamics () -> Simulation (Dynamics ())
- Simulation.Aivika.Dynamics.Base: memo :: Dynamics e -> Simulation (Dynamics e)
- Simulation.Aivika.Dynamics.Base: memo0 :: Dynamics e -> Simulation (Dynamics e)
- Simulation.Aivika.Dynamics.Base: starttime :: Dynamics Double
- Simulation.Aivika.Dynamics.Base: stoptime :: Dynamics Double
- Simulation.Aivika.Dynamics.Base: time :: Dynamics Double
- Simulation.Aivika.Dynamics.Base: umemo :: MArray IOUArray e IO => Dynamics e -> Simulation (Dynamics e)
- Simulation.Aivika.Dynamics.Base: umemo0 :: MArray IOUArray e IO => Dynamics e -> Simulation (Dynamics e)
- Simulation.Aivika.Dynamics.Buffer: bufferCount :: Buffer -> Dynamics Int
- Simulation.Aivika.Dynamics.Buffer: bufferDequeue :: Buffer -> Signal ()
- Simulation.Aivika.Dynamics.Buffer: bufferEnqueue :: Buffer -> Signal ()
- Simulation.Aivika.Dynamics.Buffer: bufferEnqueueLost :: Buffer -> Signal ()
- Simulation.Aivika.Dynamics.Buffer: bufferFull :: Buffer -> Dynamics Bool
- Simulation.Aivika.Dynamics.Buffer: bufferLostCount :: Buffer -> Dynamics Int
- Simulation.Aivika.Dynamics.Buffer: bufferMaxCount :: Buffer -> Int
- Simulation.Aivika.Dynamics.Buffer: bufferNull :: Buffer -> Dynamics Bool
- Simulation.Aivika.Dynamics.Buffer: bufferQueue :: Buffer -> EventQueue
- Simulation.Aivika.Dynamics.Buffer: data Buffer
- Simulation.Aivika.Dynamics.Buffer: dequeueBuffer :: Buffer -> Process ()
- Simulation.Aivika.Dynamics.Buffer: enqueueBuffer :: Buffer -> Process ()
- Simulation.Aivika.Dynamics.Buffer: enqueueBufferOrLost :: Buffer -> Dynamics ()
- Simulation.Aivika.Dynamics.Buffer: newBuffer :: EventQueue -> Int -> Simulation Buffer
- Simulation.Aivika.Dynamics.Buffer: tryDequeueBuffer :: Buffer -> Dynamics Bool
- Simulation.Aivika.Dynamics.Buffer: tryEnqueueBuffer :: Buffer -> Dynamics Bool
- Simulation.Aivika.Dynamics.Cont: data Cont a
- Simulation.Aivika.Dynamics.EventQueue: data EventQueue
- Simulation.Aivika.Dynamics.EventQueue: enqueue :: EventQueue -> Double -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: enqueueWithCurrentTime :: EventQueue -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: enqueueWithIntegTimes :: EventQueue -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: enqueueWithStartTime :: EventQueue -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: enqueueWithStopTime :: EventQueue -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: enqueueWithTimes :: EventQueue -> [Double] -> Dynamics () -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: newQueue :: Simulation EventQueue
- Simulation.Aivika.Dynamics.EventQueue: queueCount :: EventQueue -> Dynamics Int
- Simulation.Aivika.Dynamics.EventQueue: runQueue :: EventQueue -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: runQueueBefore :: EventQueue -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: runQueueSync :: EventQueue -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: runQueueSyncBefore :: EventQueue -> Dynamics ()
- Simulation.Aivika.Dynamics.FIFO: data FIFO a
- Simulation.Aivika.Dynamics.FIFO: dequeueFIFO :: FIFO a -> Process a
- Simulation.Aivika.Dynamics.FIFO: enqueueFIFO :: FIFO a -> a -> Process ()
- Simulation.Aivika.Dynamics.FIFO: enqueueFIFOOrLost :: FIFO a -> a -> Dynamics ()
- Simulation.Aivika.Dynamics.FIFO: fifoCount :: FIFO a -> Dynamics Int
- Simulation.Aivika.Dynamics.FIFO: fifoDequeue :: FIFO a -> Signal a
- Simulation.Aivika.Dynamics.FIFO: fifoEnqueue :: FIFO a -> Signal a
- Simulation.Aivika.Dynamics.FIFO: fifoEnqueueLost :: FIFO a -> Signal a
- Simulation.Aivika.Dynamics.FIFO: fifoFull :: FIFO a -> Dynamics Bool
- Simulation.Aivika.Dynamics.FIFO: fifoLostCount :: FIFO a -> Dynamics Int
- Simulation.Aivika.Dynamics.FIFO: fifoMaxCount :: FIFO a -> Int
- Simulation.Aivika.Dynamics.FIFO: fifoNull :: FIFO a -> Dynamics Bool
- Simulation.Aivika.Dynamics.FIFO: fifoQueue :: FIFO a -> EventQueue
- Simulation.Aivika.Dynamics.FIFO: newFIFO :: EventQueue -> Int -> Simulation (FIFO a)
- Simulation.Aivika.Dynamics.FIFO: tryDequeueFIFO :: FIFO a -> Dynamics (Maybe a)
- Simulation.Aivika.Dynamics.FIFO: tryEnqueueFIFO :: FIFO a -> a -> Dynamics Bool
- Simulation.Aivika.Dynamics.LIFO: data LIFO a
- Simulation.Aivika.Dynamics.LIFO: dequeueLIFO :: LIFO a -> Process a
- Simulation.Aivika.Dynamics.LIFO: enqueueLIFO :: LIFO a -> a -> Process ()
- Simulation.Aivika.Dynamics.LIFO: enqueueLIFOOrLost :: LIFO a -> a -> Dynamics ()
- Simulation.Aivika.Dynamics.LIFO: lifoCount :: LIFO a -> Dynamics Int
- Simulation.Aivika.Dynamics.LIFO: lifoDequeue :: LIFO a -> Signal a
- Simulation.Aivika.Dynamics.LIFO: lifoEnqueue :: LIFO a -> Signal a
- Simulation.Aivika.Dynamics.LIFO: lifoEnqueueLost :: LIFO a -> Signal a
- Simulation.Aivika.Dynamics.LIFO: lifoFull :: LIFO a -> Dynamics Bool
- Simulation.Aivika.Dynamics.LIFO: lifoLostCount :: LIFO a -> Dynamics Int
- Simulation.Aivika.Dynamics.LIFO: lifoMaxCount :: LIFO a -> Int
- Simulation.Aivika.Dynamics.LIFO: lifoNull :: LIFO a -> Dynamics Bool
- Simulation.Aivika.Dynamics.LIFO: lifoQueue :: LIFO a -> EventQueue
- Simulation.Aivika.Dynamics.LIFO: newLIFO :: EventQueue -> Int -> Simulation (LIFO a)
- Simulation.Aivika.Dynamics.LIFO: tryDequeueLIFO :: LIFO a -> Dynamics (Maybe a)
- Simulation.Aivika.Dynamics.LIFO: tryEnqueueLIFO :: LIFO a -> a -> Dynamics Bool
- Simulation.Aivika.Dynamics.Parameter: newIndexedParameter :: (Int -> IO a) -> IO (Simulation a)
- Simulation.Aivika.Dynamics.Parameter: newNormalParameter :: Simulation Double -> Simulation Double -> IO (Simulation Double)
- Simulation.Aivika.Dynamics.Parameter: newParameter :: IO a -> IO (Simulation a)
- Simulation.Aivika.Dynamics.Parameter: newRandomParameter :: Simulation Double -> Simulation Double -> IO (Simulation Double)
- Simulation.Aivika.Dynamics.Parameter: newTableParameter :: Array Int a -> IO (Simulation a)
- Simulation.Aivika.Dynamics.Process: cancelProcess :: ProcessID -> Dynamics ()
- Simulation.Aivika.Dynamics.Process: catchProcess :: Process a -> (IOException -> Process a) -> Process a
- Simulation.Aivika.Dynamics.Process: data Process a
- Simulation.Aivika.Dynamics.Process: data ProcessID
- Simulation.Aivika.Dynamics.Process: finallyProcess :: Process a -> Process b -> Process a
- Simulation.Aivika.Dynamics.Process: holdProcess :: Double -> Process ()
- Simulation.Aivika.Dynamics.Process: interruptProcess :: ProcessID -> Dynamics ()
- Simulation.Aivika.Dynamics.Process: newProcessID :: EventQueue -> Simulation ProcessID
- Simulation.Aivika.Dynamics.Process: newProcessIDWithCatch :: EventQueue -> Simulation ProcessID
- Simulation.Aivika.Dynamics.Process: passivateProcess :: Process ()
- Simulation.Aivika.Dynamics.Process: processCanceled :: ProcessID -> Dynamics Bool
- Simulation.Aivika.Dynamics.Process: processID :: Process ProcessID
- Simulation.Aivika.Dynamics.Process: processInterrupted :: ProcessID -> Dynamics Bool
- Simulation.Aivika.Dynamics.Process: processPassive :: ProcessID -> Dynamics Bool
- Simulation.Aivika.Dynamics.Process: processQueue :: ProcessID -> EventQueue
- Simulation.Aivika.Dynamics.Process: reactivateProcess :: ProcessID -> Dynamics ()
- Simulation.Aivika.Dynamics.Process: runProcess :: Process () -> ProcessID -> Double -> Dynamics ()
- Simulation.Aivika.Dynamics.Process: runProcessNow :: Process () -> ProcessID -> Dynamics ()
- Simulation.Aivika.Dynamics.Process: throwProcess :: IOException -> Process a
- Simulation.Aivika.Dynamics.Random: newNormal :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.Random: newRandom :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.Random: normalGen :: IO (IO Double)
- Simulation.Aivika.Dynamics.Ref: data Ref a
- Simulation.Aivika.Dynamics.Ref: modifyRef :: Ref a -> (a -> a) -> Dynamics ()
- Simulation.Aivika.Dynamics.Ref: newRef :: EventQueue -> a -> Simulation (Ref a)
- Simulation.Aivika.Dynamics.Ref: readRef :: Ref a -> Dynamics a
- Simulation.Aivika.Dynamics.Ref: refChanged :: Ref a -> Signal a
- Simulation.Aivika.Dynamics.Ref: refChanged_ :: Ref a -> Signal ()
- Simulation.Aivika.Dynamics.Ref: refQueue :: Ref a -> EventQueue
- Simulation.Aivika.Dynamics.Ref: writeRef :: Ref a -> a -> Dynamics ()
- Simulation.Aivika.Dynamics.Resource: data Resource
- Simulation.Aivika.Dynamics.Resource: instance Eq Resource
- Simulation.Aivika.Dynamics.Resource: newResource :: EventQueue -> Int -> Simulation Resource
- Simulation.Aivika.Dynamics.Resource: newResourceWithCount :: EventQueue -> Int -> Int -> Simulation Resource
- Simulation.Aivika.Dynamics.Resource: releaseResource :: Resource -> Process ()
- Simulation.Aivika.Dynamics.Resource: releaseResourceInDynamics :: Resource -> Dynamics ()
- Simulation.Aivika.Dynamics.Resource: requestResource :: Resource -> Process ()
- Simulation.Aivika.Dynamics.Resource: resourceCount :: Resource -> Dynamics Int
- Simulation.Aivika.Dynamics.Resource: resourceInitCount :: Resource -> Int
- Simulation.Aivika.Dynamics.Resource: resourceQueue :: Resource -> EventQueue
- Simulation.Aivika.Dynamics.Resource: tryRequestResourceInDynamics :: Resource -> Dynamics Bool
- Simulation.Aivika.Dynamics.Resource: usingResource :: Resource -> Process a -> Process a
- Simulation.Aivika.Dynamics.Signal: apSignal :: Dynamics (a -> b) -> Signal a -> Signal b
- Simulation.Aivika.Dynamics.Signal: awaitSignal :: Signal a -> Process a
- Simulation.Aivika.Dynamics.Signal: data Signal a
- Simulation.Aivika.Dynamics.Signal: data SignalHistory a
- Simulation.Aivika.Dynamics.Signal: data SignalSource a
- Simulation.Aivika.Dynamics.Signal: emptySignal :: Signal a
- Simulation.Aivika.Dynamics.Signal: filterSignal :: (a -> Bool) -> Signal a -> Signal a
- Simulation.Aivika.Dynamics.Signal: filterSignalM :: (a -> Dynamics Bool) -> Signal a -> Signal a
- Simulation.Aivika.Dynamics.Signal: handleSignal :: Signal a -> (a -> Dynamics ()) -> Dynamics (Dynamics ())
- Simulation.Aivika.Dynamics.Signal: handleSignal_ :: Signal a -> (a -> Dynamics ()) -> Dynamics ()
- Simulation.Aivika.Dynamics.Signal: mapSignal :: (a -> b) -> Signal a -> Signal b
- Simulation.Aivika.Dynamics.Signal: mapSignalM :: (a -> Dynamics b) -> Signal a -> Signal b
- Simulation.Aivika.Dynamics.Signal: merge2Signals :: Signal a -> Signal a -> Signal a
- Simulation.Aivika.Dynamics.Signal: merge3Signals :: Signal a -> Signal a -> Signal a -> Signal a
- Simulation.Aivika.Dynamics.Signal: merge4Signals :: Signal a -> Signal a -> Signal a -> Signal a -> Signal a
- Simulation.Aivika.Dynamics.Signal: merge5Signals :: Signal a -> Signal a -> Signal a -> Signal a -> Signal a -> Signal a
- Simulation.Aivika.Dynamics.Signal: newSignalHistory :: Signal a -> Dynamics (SignalHistory a)
- Simulation.Aivika.Dynamics.Signal: newSignalHistoryThrough :: EventQueue -> Signal a -> Dynamics (SignalHistory a)
- Simulation.Aivika.Dynamics.Signal: newSignalInIntegTimes :: EventQueue -> Dynamics (Signal Double)
- Simulation.Aivika.Dynamics.Signal: newSignalInStartTime :: EventQueue -> Dynamics (Signal Double)
- Simulation.Aivika.Dynamics.Signal: newSignalInStopTime :: EventQueue -> Dynamics (Signal Double)
- Simulation.Aivika.Dynamics.Signal: newSignalInTimes :: EventQueue -> [Double] -> Dynamics (Signal Double)
- Simulation.Aivika.Dynamics.Signal: newSignalSource :: EventQueue -> Simulation (SignalSource a)
- Simulation.Aivika.Dynamics.Signal: newSignalSourceWithUpdate :: Dynamics () -> Simulation (SignalSource a)
- Simulation.Aivika.Dynamics.Signal: publishSignal :: SignalSource a -> Signal a
- Simulation.Aivika.Dynamics.Signal: readSignalHistory :: SignalHistory a -> Dynamics (Array Int Double, Array Int a)
- Simulation.Aivika.Dynamics.Signal: signalHistorySignal :: SignalHistory a -> Signal a
- Simulation.Aivika.Dynamics.Signal: triggerSignal :: SignalSource a -> a -> Dynamics ()
- Simulation.Aivika.Dynamics.Signal: updateSignal :: Signal a -> Dynamics ()
- Simulation.Aivika.Dynamics.Simulation: Euler :: Method
- Simulation.Aivika.Dynamics.Simulation: RungeKutta2 :: Method
- Simulation.Aivika.Dynamics.Simulation: RungeKutta4 :: Method
- Simulation.Aivika.Dynamics.Simulation: Specs :: Double -> Double -> Double -> Method -> Specs
- Simulation.Aivika.Dynamics.Simulation: catchSimulation :: Simulation a -> (IOException -> Simulation a) -> Simulation a
- Simulation.Aivika.Dynamics.Simulation: class Monad m => SimulationLift m
- Simulation.Aivika.Dynamics.Simulation: data Method
- Simulation.Aivika.Dynamics.Simulation: data Simulation a
- Simulation.Aivika.Dynamics.Simulation: data Specs
- Simulation.Aivika.Dynamics.Simulation: finallySimulation :: Simulation a -> Simulation b -> Simulation a
- Simulation.Aivika.Dynamics.Simulation: liftSimulation :: SimulationLift m => Simulation a -> m a
- Simulation.Aivika.Dynamics.Simulation: runSimulation :: Simulation a -> Specs -> IO a
- Simulation.Aivika.Dynamics.Simulation: runSimulations :: Simulation a -> Specs -> Int -> [IO a]
- Simulation.Aivika.Dynamics.Simulation: simulationCount :: Simulation Int
- Simulation.Aivika.Dynamics.Simulation: simulationIndex :: Simulation Int
- Simulation.Aivika.Dynamics.Simulation: simulationSpecs :: Simulation Specs
- Simulation.Aivika.Dynamics.Simulation: spcDT :: Specs -> Double
- Simulation.Aivika.Dynamics.Simulation: spcMethod :: Specs -> Method
- Simulation.Aivika.Dynamics.Simulation: spcStartTime :: Specs -> Double
- Simulation.Aivika.Dynamics.Simulation: spcStopTime :: Specs -> Double
- Simulation.Aivika.Dynamics.Simulation: throwSimulation :: IOException -> Simulation a
- Simulation.Aivika.Dynamics.SystemDynamics: (./=.) :: Eq a => Dynamics a -> Dynamics a -> Dynamics Bool
- Simulation.Aivika.Dynamics.SystemDynamics: (.<.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
- Simulation.Aivika.Dynamics.SystemDynamics: (.<=.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
- Simulation.Aivika.Dynamics.SystemDynamics: (.==.) :: Eq a => Dynamics a -> Dynamics a -> Dynamics Bool
- Simulation.Aivika.Dynamics.SystemDynamics: (.>.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
- Simulation.Aivika.Dynamics.SystemDynamics: (.>=.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
- Simulation.Aivika.Dynamics.SystemDynamics: data Integ
- Simulation.Aivika.Dynamics.SystemDynamics: data Sum a
- Simulation.Aivika.Dynamics.SystemDynamics: delay :: Dynamics a -> Dynamics Double -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.SystemDynamics: delay1 :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: delay1I :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: delay3 :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: delay3I :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: delayI :: Dynamics a -> Dynamics Double -> Dynamics a -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.SystemDynamics: delayN :: Dynamics Double -> Dynamics Double -> Int -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: delayNI :: Dynamics Double -> Dynamics Double -> Int -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: delayTrans :: Dynamics a -> Dynamics Double -> Dynamics a -> (Dynamics a -> Simulation (Dynamics a)) -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.SystemDynamics: forecast :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: ifDynamics :: Dynamics Bool -> Dynamics a -> Dynamics a -> Dynamics a
- Simulation.Aivika.Dynamics.SystemDynamics: integ :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: integDiff :: Integ -> Dynamics Double -> Simulation ()
- Simulation.Aivika.Dynamics.SystemDynamics: integInit :: Integ -> Dynamics Double
- Simulation.Aivika.Dynamics.SystemDynamics: integValue :: Integ -> Dynamics Double
- Simulation.Aivika.Dynamics.SystemDynamics: lookupD :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double
- Simulation.Aivika.Dynamics.SystemDynamics: lookupDynamics :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double
- Simulation.Aivika.Dynamics.SystemDynamics: lookupStepwiseD :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double
- Simulation.Aivika.Dynamics.SystemDynamics: lookupStepwiseDynamics :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double
- Simulation.Aivika.Dynamics.SystemDynamics: maxDynamics :: Ord a => Dynamics a -> Dynamics a -> Dynamics a
- Simulation.Aivika.Dynamics.SystemDynamics: minDynamics :: Ord a => Dynamics a -> Dynamics a -> Dynamics a
- Simulation.Aivika.Dynamics.SystemDynamics: newInteg :: Dynamics Double -> Simulation Integ
- Simulation.Aivika.Dynamics.SystemDynamics: newSum :: (MArray IOUArray a IO, Num a) => Dynamics a -> Simulation (Sum a)
- Simulation.Aivika.Dynamics.SystemDynamics: npv :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: npve :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: smooth :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: smooth3 :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: smooth3I :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: smoothI :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: smoothN :: Dynamics Double -> Dynamics Double -> Int -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: smoothNI :: Dynamics Double -> Dynamics Double -> Int -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: sumDiff :: (MArray IOUArray a IO, Num a) => Sum a -> Dynamics a -> Simulation ()
- Simulation.Aivika.Dynamics.SystemDynamics: sumDynamics :: (MArray IOUArray a IO, Num a) => Dynamics a -> Dynamics a -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.SystemDynamics: sumInit :: Sum a -> Dynamics a
- Simulation.Aivika.Dynamics.SystemDynamics: sumValue :: Sum a -> Dynamics a
- Simulation.Aivika.Dynamics.SystemDynamics: trend :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
- Simulation.Aivika.Dynamics.SystemDynamics: udelay :: (MArray IOUArray a IO, Num a) => Dynamics a -> Dynamics Double -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.SystemDynamics: udelayI :: (MArray IOUArray a IO, Num a) => Dynamics a -> Dynamics Double -> Dynamics a -> Simulation (Dynamics a)
- Simulation.Aivika.Dynamics.UVar: data UVar a
- Simulation.Aivika.Dynamics.UVar: freezeUVar :: MArray IOUArray a IO => UVar a -> Dynamics (Array Int Double, Array Int a)
- Simulation.Aivika.Dynamics.UVar: modifyUVar :: MArray IOUArray a IO => UVar a -> (a -> a) -> Dynamics ()
- Simulation.Aivika.Dynamics.UVar: newUVar :: MArray IOUArray a IO => EventQueue -> a -> Simulation (UVar a)
- Simulation.Aivika.Dynamics.UVar: readUVar :: MArray IOUArray a IO => UVar a -> Dynamics a
- Simulation.Aivika.Dynamics.UVar: uvarChanged :: UVar a -> Signal a
- Simulation.Aivika.Dynamics.UVar: uvarChanged_ :: UVar a -> Signal ()
- Simulation.Aivika.Dynamics.UVar: uvarQueue :: UVar a -> EventQueue
- Simulation.Aivika.Dynamics.UVar: writeUVar :: MArray IOUArray a IO => UVar a -> a -> Dynamics ()
- Simulation.Aivika.Dynamics.Var: data Var a
- Simulation.Aivika.Dynamics.Var: freezeVar :: Var a -> Dynamics (Array Int Double, Array Int a)
- Simulation.Aivika.Dynamics.Var: modifyVar :: Var a -> (a -> a) -> Dynamics ()
- Simulation.Aivika.Dynamics.Var: newVar :: EventQueue -> a -> Simulation (Var a)
- Simulation.Aivika.Dynamics.Var: readVar :: Var a -> Dynamics a
- Simulation.Aivika.Dynamics.Var: varChanged :: Var a -> Signal a
- Simulation.Aivika.Dynamics.Var: varChanged_ :: Var a -> Signal ()
- Simulation.Aivika.Dynamics.Var: varQueue :: Var a -> EventQueue
- Simulation.Aivika.Dynamics.Var: writeVar :: Var a -> a -> Dynamics ()
- Simulation.Aivika.Queue: queueFront :: Queue a -> IO a
+ Simulation.Aivika.Agent: activateState :: AgentState -> Event ()
+ Simulation.Aivika.Agent: addTimeout :: AgentState -> Double -> Event () -> Event ()
+ Simulation.Aivika.Agent: addTimer :: AgentState -> Event Double -> Event () -> Event ()
+ Simulation.Aivika.Agent: agentState :: Agent -> Event (Maybe AgentState)
+ Simulation.Aivika.Agent: agentStateChanged :: Agent -> Signal (Maybe AgentState)
+ Simulation.Aivika.Agent: agentStateChanged_ :: Agent -> Signal ()
+ Simulation.Aivika.Agent: data Agent
+ Simulation.Aivika.Agent: data AgentState
+ Simulation.Aivika.Agent: instance Eq Agent
+ Simulation.Aivika.Agent: instance Eq AgentState
+ Simulation.Aivika.Agent: newAgent :: Simulation Agent
+ Simulation.Aivika.Agent: newState :: Agent -> Simulation AgentState
+ Simulation.Aivika.Agent: newSubstate :: AgentState -> Simulation AgentState
+ Simulation.Aivika.Agent: setStateActivation :: AgentState -> Event () -> Simulation ()
+ Simulation.Aivika.Agent: setStateDeactivation :: AgentState -> Event () -> Simulation ()
+ Simulation.Aivika.Agent: setStateTransition :: AgentState -> Event (Maybe AgentState) -> Simulation ()
+ Simulation.Aivika.Agent: stateAgent :: AgentState -> Agent
+ Simulation.Aivika.Agent: stateParent :: AgentState -> Maybe AgentState
+ Simulation.Aivika.Cont: data Cont a
+ Simulation.Aivika.DoubleLinkedList: data DoubleLinkedList a
+ Simulation.Aivika.DoubleLinkedList: listAddLast :: DoubleLinkedList a -> a -> IO ()
+ Simulation.Aivika.DoubleLinkedList: listCount :: DoubleLinkedList a -> IO Int
+ Simulation.Aivika.DoubleLinkedList: listFirst :: DoubleLinkedList a -> IO a
+ Simulation.Aivika.DoubleLinkedList: listInsertFirst :: DoubleLinkedList a -> a -> IO ()
+ Simulation.Aivika.DoubleLinkedList: listLast :: DoubleLinkedList a -> IO a
+ Simulation.Aivika.DoubleLinkedList: listNull :: DoubleLinkedList a -> IO Bool
+ Simulation.Aivika.DoubleLinkedList: listRemoveFirst :: DoubleLinkedList a -> IO ()
+ Simulation.Aivika.DoubleLinkedList: listRemoveLast :: DoubleLinkedList a -> IO ()
+ Simulation.Aivika.DoubleLinkedList: newList :: IO (DoubleLinkedList a)
+ Simulation.Aivika.Dynamics: dt :: Dynamics Double
+ Simulation.Aivika.Dynamics: integIteration :: Dynamics Int
+ Simulation.Aivika.Dynamics: integPhase :: Dynamics Int
+ Simulation.Aivika.Dynamics: isTimeInteg :: Dynamics Bool
+ Simulation.Aivika.Dynamics: starttime :: Dynamics Double
+ Simulation.Aivika.Dynamics: stoptime :: Dynamics Double
+ Simulation.Aivika.Dynamics: time :: Dynamics Double
+ Simulation.Aivika.Dynamics.Fold: foldDynamics :: (Dynamics a -> Simulation (Dynamics a)) -> (a -> b -> a) -> a -> Dynamics b -> Simulation (Dynamics a)
+ Simulation.Aivika.Dynamics.Fold: foldDynamics1 :: (Dynamics a -> Simulation (Dynamics a)) -> (a -> a -> a) -> Dynamics a -> Simulation (Dynamics a)
+ Simulation.Aivika.Dynamics.Interpolate: discreteDynamics :: Dynamics a -> Dynamics a
+ Simulation.Aivika.Dynamics.Interpolate: initDynamics :: Dynamics a -> Dynamics a
+ Simulation.Aivika.Dynamics.Interpolate: interpolateDynamics :: Dynamics a -> Dynamics a
+ Simulation.Aivika.Dynamics.Memo: iterateDynamics :: Dynamics () -> Simulation (Dynamics ())
+ Simulation.Aivika.Dynamics.Memo: memo0Dynamics :: Dynamics e -> Simulation (Dynamics e)
+ Simulation.Aivika.Dynamics.Memo: memoDynamics :: Dynamics e -> Simulation (Dynamics e)
+ Simulation.Aivika.Dynamics.Memo.Unboxed: memo0Dynamics :: Unboxed e => Dynamics e -> Simulation (Dynamics e)
+ Simulation.Aivika.Dynamics.Memo.Unboxed: memoDynamics :: Unboxed e => Dynamics e -> Simulation (Dynamics e)
+ Simulation.Aivika.Dynamics.Random: newNormalDynamics :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.Dynamics.Random: newRandomDynamics :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.Event: EventCancellation :: Event () -> Event Bool -> Event Bool -> EventCancellation
+ Simulation.Aivika.Event: IncludingCurrentEvents :: EventProcessing
+ Simulation.Aivika.Event: IncludingCurrentEventsOrFromPast :: EventProcessing
+ Simulation.Aivika.Event: IncludingEarlierEvents :: EventProcessing
+ Simulation.Aivika.Event: IncludingEarlierEventsOrFromPast :: EventProcessing
+ Simulation.Aivika.Event: cancelEvent :: EventCancellation -> Event ()
+ Simulation.Aivika.Event: catchEvent :: Event a -> (IOException -> Event a) -> Event a
+ Simulation.Aivika.Event: class Monad m => EventLift m
+ Simulation.Aivika.Event: data Event a
+ Simulation.Aivika.Event: data EventCancellation
+ Simulation.Aivika.Event: data EventProcessing
+ Simulation.Aivika.Event: enqueueEvent :: Double -> Event () -> Event ()
+ Simulation.Aivika.Event: enqueueEventWithCancellation :: Double -> Event () -> Event EventCancellation
+ Simulation.Aivika.Event: enqueueEventWithCurrentTime :: Event () -> Event ()
+ Simulation.Aivika.Event: enqueueEventWithIntegTimes :: Event () -> Event ()
+ Simulation.Aivika.Event: enqueueEventWithStartTime :: Event () -> Event ()
+ Simulation.Aivika.Event: enqueueEventWithStopTime :: Event () -> Event ()
+ Simulation.Aivika.Event: enqueueEventWithTimes :: [Double] -> Event () -> Event ()
+ Simulation.Aivika.Event: eventCanceled :: EventCancellation -> Event Bool
+ Simulation.Aivika.Event: eventFinished :: EventCancellation -> Event Bool
+ Simulation.Aivika.Event: eventQueueCount :: Event Int
+ Simulation.Aivika.Event: finallyEvent :: Event a -> Event b -> Event a
+ Simulation.Aivika.Event: liftEvent :: EventLift m => Event a -> m a
+ Simulation.Aivika.Event: runEvent :: EventProcessing -> Event a -> Dynamics a
+ Simulation.Aivika.Event: runEventInStartTime :: EventProcessing -> Event a -> Simulation a
+ Simulation.Aivika.Event: runEventInStopTime :: EventProcessing -> Event a -> Simulation a
+ Simulation.Aivika.Event: throwEvent :: IOException -> Event a
+ Simulation.Aivika.Parameter: newIndexedParameter :: (Int -> IO a) -> IO (Simulation a)
+ Simulation.Aivika.Parameter: newParameter :: IO a -> IO (Simulation a)
+ Simulation.Aivika.Parameter: newTableParameter :: Array Int a -> IO (Simulation a)
+ Simulation.Aivika.Parameter.Random: newNormalParameter :: Simulation Double -> Simulation Double -> IO (Simulation Double)
+ Simulation.Aivika.Parameter.Random: newRandomParameter :: Simulation Double -> Simulation Double -> IO (Simulation Double)
+ Simulation.Aivika.Process: cancelProcess :: ProcessId -> Event ()
+ Simulation.Aivika.Process: catchProcess :: Process a -> (IOException -> Process a) -> Process a
+ Simulation.Aivika.Process: data Process a
+ Simulation.Aivika.Process: data ProcessId
+ Simulation.Aivika.Process: enqueueProcess :: Double -> ProcessId -> Process () -> Event ()
+ Simulation.Aivika.Process: enqueueProcessWithStartTime :: ProcessId -> Process () -> Event ()
+ Simulation.Aivika.Process: enqueueProcessWithStopTime :: ProcessId -> Process () -> Event ()
+ Simulation.Aivika.Process: finallyProcess :: Process a -> Process b -> Process a
+ Simulation.Aivika.Process: holdProcess :: Double -> Process ()
+ Simulation.Aivika.Process: interruptProcess :: ProcessId -> Event ()
+ Simulation.Aivika.Process: newProcessId :: Simulation ProcessId
+ Simulation.Aivika.Process: newProcessIdWithCatch :: Simulation ProcessId
+ Simulation.Aivika.Process: passivateProcess :: Process ()
+ Simulation.Aivika.Process: processCanceled :: ProcessId -> Event Bool
+ Simulation.Aivika.Process: processId :: Process ProcessId
+ Simulation.Aivika.Process: processInterrupted :: ProcessId -> Event Bool
+ Simulation.Aivika.Process: processPassive :: ProcessId -> Event Bool
+ Simulation.Aivika.Process: reactivateProcess :: ProcessId -> Event ()
+ Simulation.Aivika.Process: runProcess :: ProcessId -> Process () -> Event ()
+ Simulation.Aivika.Process: runProcessInStartTime :: EventProcessing -> ProcessId -> Process () -> Simulation ()
+ Simulation.Aivika.Process: runProcessInStopTime :: EventProcessing -> ProcessId -> Process () -> Simulation ()
+ Simulation.Aivika.Process: throwProcess :: IOException -> Process a
+ Simulation.Aivika.Queue: dequeueWithDynamicPriority :: (DequeueStrategy si qi, DequeueStrategy sm qm, DynamicPriorityQueueStrategy so qo) => Queue si qi sm qm so qo a -> Event Double -> Process a
+ Simulation.Aivika.Queue: dequeueWithPriority :: (DequeueStrategy si qi, DequeueStrategy sm qm, PriorityQueueStrategy so qo) => Queue si qi sm qm so qo a -> Double -> Process a
+ Simulation.Aivika.Queue: dequeued :: Queue si qi sm qm so qo a -> Signal a
+ Simulation.Aivika.Queue: enqueueOrLost :: (EnqueueStrategy sm qm, DequeueStrategy so qo) => Queue si qi sm qm so qo a -> a -> Event Bool
+ Simulation.Aivika.Queue: enqueueOrLost_ :: (EnqueueStrategy sm qm, DequeueStrategy so qo) => Queue si qi sm qm so qo a -> a -> Event ()
+ Simulation.Aivika.Queue: enqueueWithDynamicPriority :: (DynamicPriorityQueueStrategy si qi, EnqueueStrategy sm qm, DequeueStrategy so qo) => Queue si qi sm qm so qo a -> Event Double -> a -> Process ()
+ Simulation.Aivika.Queue: enqueueWithPriority :: (PriorityQueueStrategy si qi, EnqueueStrategy sm qm, DequeueStrategy so qo) => Queue si qi sm qm so qo a -> Double -> a -> Process ()
+ Simulation.Aivika.Queue: enqueued :: Queue si qi sm qm so qo a -> Signal a
+ Simulation.Aivika.Queue: enqueuedButLost :: Queue si qi sm qm so qo a -> Signal a
+ Simulation.Aivika.Queue: queueFull :: Queue si qi sm qm so qo a -> Event Bool
+ Simulation.Aivika.Queue: queueLostCount :: Queue si qi sm qm so qo a -> Event Int
+ Simulation.Aivika.Queue: queueMaxCount :: Queue si qi sm qm so qo a -> Int
+ Simulation.Aivika.Queue: tryDequeue :: (DequeueStrategy si qi, DequeueStrategy sm qm) => Queue si qi sm qm so qo a -> Event (Maybe a)
+ Simulation.Aivika.Queue: tryEnqueue :: (EnqueueStrategy sm qm, DequeueStrategy so qo) => Queue si qi sm qm so qo a -> a -> Event Bool
+ Simulation.Aivika.QueueStrategy: FCFS :: FCFS
+ Simulation.Aivika.QueueStrategy: LCFS :: LCFS
+ Simulation.Aivika.QueueStrategy: SIRO :: SIRO
+ Simulation.Aivika.QueueStrategy: StaticPriorities :: StaticPriorities
+ Simulation.Aivika.QueueStrategy: class QueueStrategy s q => DequeueStrategy s q | s -> q
+ Simulation.Aivika.QueueStrategy: class DequeueStrategy s q => DynamicPriorityQueueStrategy s q | s -> q
+ Simulation.Aivika.QueueStrategy: class DequeueStrategy s q => EnqueueStrategy s q | s -> q
+ Simulation.Aivika.QueueStrategy: class DequeueStrategy s q => PriorityQueueStrategy s q | s -> q
+ Simulation.Aivika.QueueStrategy: class QueueStrategy s q | s -> q
+ Simulation.Aivika.QueueStrategy: data FCFS
+ Simulation.Aivika.QueueStrategy: data LCFS
+ Simulation.Aivika.QueueStrategy: data SIRO
+ Simulation.Aivika.QueueStrategy: data StaticPriorities
+ Simulation.Aivika.QueueStrategy: instance DequeueStrategy FCFS DoubleLinkedList
+ Simulation.Aivika.QueueStrategy: instance DequeueStrategy LCFS DoubleLinkedList
+ Simulation.Aivika.QueueStrategy: instance DequeueStrategy SIRO Vector
+ Simulation.Aivika.QueueStrategy: instance DequeueStrategy StaticPriorities PriorityQueue
+ Simulation.Aivika.QueueStrategy: instance EnqueueStrategy FCFS DoubleLinkedList
+ Simulation.Aivika.QueueStrategy: instance EnqueueStrategy LCFS DoubleLinkedList
+ Simulation.Aivika.QueueStrategy: instance EnqueueStrategy SIRO Vector
+ Simulation.Aivika.QueueStrategy: instance PriorityQueueStrategy StaticPriorities PriorityQueue
+ Simulation.Aivika.QueueStrategy: instance QueueStrategy FCFS DoubleLinkedList
+ Simulation.Aivika.QueueStrategy: instance QueueStrategy LCFS DoubleLinkedList
+ Simulation.Aivika.QueueStrategy: instance QueueStrategy SIRO Vector
+ Simulation.Aivika.QueueStrategy: instance QueueStrategy StaticPriorities PriorityQueue
+ Simulation.Aivika.QueueStrategy: newStrategyQueue :: QueueStrategy s q => s -> Simulation (q i)
+ Simulation.Aivika.QueueStrategy: strategyDequeue :: DequeueStrategy s q => s -> q i -> Event i
+ Simulation.Aivika.QueueStrategy: strategyEnqueue :: EnqueueStrategy s q => s -> q i -> i -> Event ()
+ Simulation.Aivika.QueueStrategy: strategyEnqueueWithDynamicPriority :: DynamicPriorityQueueStrategy s q => s -> q i -> Event Double -> i -> Event ()
+ Simulation.Aivika.QueueStrategy: strategyEnqueueWithPriority :: PriorityQueueStrategy s q => s -> q i -> Double -> i -> Event ()
+ Simulation.Aivika.QueueStrategy: strategyQueueNull :: QueueStrategy s q => s -> q i -> Event Bool
+ Simulation.Aivika.Random: newNormalGen :: IO (IO Double)
+ Simulation.Aivika.Ref: data Ref a
+ Simulation.Aivika.Ref: modifyRef :: Ref a -> (a -> a) -> Event ()
+ Simulation.Aivika.Ref: newRef :: a -> Simulation (Ref a)
+ Simulation.Aivika.Ref: readRef :: Ref a -> Event a
+ Simulation.Aivika.Ref: refChanged :: Ref a -> Signal a
+ Simulation.Aivika.Ref: refChanged_ :: Ref a -> Signal ()
+ Simulation.Aivika.Ref: writeRef :: Ref a -> a -> Event ()
+ Simulation.Aivika.Resource: data Resource s q
+ Simulation.Aivika.Resource: instance Eq (Resource s q)
+ Simulation.Aivika.Resource: newResource :: QueueStrategy s q => s -> Int -> Simulation (Resource s q)
+ Simulation.Aivika.Resource: newResourceWithCount :: QueueStrategy s q => s -> Int -> Int -> Simulation (Resource s q)
+ Simulation.Aivika.Resource: releaseResource :: DequeueStrategy s q => Resource s q -> Process ()
+ Simulation.Aivika.Resource: releaseResourceWithinEvent :: DequeueStrategy s q => Resource s q -> Event ()
+ Simulation.Aivika.Resource: requestResource :: EnqueueStrategy s q => Resource s q -> Process ()
+ Simulation.Aivika.Resource: requestResourceWithDynamicPriority :: DynamicPriorityQueueStrategy s q => Resource s q -> Event Double -> Process ()
+ Simulation.Aivika.Resource: requestResourceWithPriority :: PriorityQueueStrategy s q => Resource s q -> Double -> Process ()
+ Simulation.Aivika.Resource: resourceCount :: Resource s q -> Event Int
+ Simulation.Aivika.Resource: resourceMaxCount :: Resource s q -> Int
+ Simulation.Aivika.Resource: tryRequestResourceWithinEvent :: Resource s q -> Event Bool
+ Simulation.Aivika.Resource: usingResource :: EnqueueStrategy s q => Resource s q -> Process a -> Process a
+ Simulation.Aivika.Resource: usingResourceWithDynamicPriority :: DynamicPriorityQueueStrategy s q => Resource s q -> Event Double -> Process a -> Process a
+ Simulation.Aivika.Resource: usingResourceWithPriority :: PriorityQueueStrategy s q => Resource s q -> Double -> Process a -> Process a
+ Simulation.Aivika.Signal: Signal :: ((a -> Event ()) -> Event (Event ())) -> Signal a
+ Simulation.Aivika.Signal: apSignal :: Event (a -> b) -> Signal a -> Signal b
+ Simulation.Aivika.Signal: awaitSignal :: Signal a -> Process a
+ Simulation.Aivika.Signal: data Signal a
+ Simulation.Aivika.Signal: data SignalHistory a
+ Simulation.Aivika.Signal: data SignalSource a
+ Simulation.Aivika.Signal: emptySignal :: Signal a
+ Simulation.Aivika.Signal: filterSignal :: (a -> Bool) -> Signal a -> Signal a
+ Simulation.Aivika.Signal: filterSignalM :: (a -> Event Bool) -> Signal a -> Signal a
+ Simulation.Aivika.Signal: handleSignal :: Signal a -> (a -> Event ()) -> Event (Event ())
+ Simulation.Aivika.Signal: handleSignal_ :: Signal a -> (a -> Event ()) -> Event ()
+ Simulation.Aivika.Signal: mapSignal :: (a -> b) -> Signal a -> Signal b
+ Simulation.Aivika.Signal: mapSignalM :: (a -> Event b) -> Signal a -> Signal b
+ Simulation.Aivika.Signal: merge2Signals :: Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Signal: merge3Signals :: Signal a -> Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Signal: merge4Signals :: Signal a -> Signal a -> Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Signal: merge5Signals :: Signal a -> Signal a -> Signal a -> Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Signal: newSignalHistory :: Signal a -> Event (SignalHistory a)
+ Simulation.Aivika.Signal: newSignalInIntegTimes :: Event (Signal Double)
+ Simulation.Aivika.Signal: newSignalInStartTime :: Event (Signal Double)
+ Simulation.Aivika.Signal: newSignalInStopTime :: Event (Signal Double)
+ Simulation.Aivika.Signal: newSignalInTimes :: [Double] -> Event (Signal Double)
+ Simulation.Aivika.Signal: newSignalSource :: Simulation (SignalSource a)
+ Simulation.Aivika.Signal: publishSignal :: SignalSource a -> Signal a
+ Simulation.Aivika.Signal: readSignalHistory :: SignalHistory a -> Event (Array Int Double, Array Int a)
+ Simulation.Aivika.Signal: signalHistorySignal :: SignalHistory a -> Signal a
+ Simulation.Aivika.Signal: triggerSignal :: SignalSource a -> a -> Event ()
+ Simulation.Aivika.Simulation: catchSimulation :: Simulation a -> (IOException -> Simulation a) -> Simulation a
+ Simulation.Aivika.Simulation: class Monad m => SimulationLift m
+ Simulation.Aivika.Simulation: data Simulation a
+ Simulation.Aivika.Simulation: finallySimulation :: Simulation a -> Simulation b -> Simulation a
+ Simulation.Aivika.Simulation: liftSimulation :: SimulationLift m => Simulation a -> m a
+ Simulation.Aivika.Simulation: runSimulation :: Simulation a -> Specs -> IO a
+ Simulation.Aivika.Simulation: runSimulations :: Simulation a -> Specs -> Int -> [IO a]
+ Simulation.Aivika.Simulation: simulationCount :: Simulation Int
+ Simulation.Aivika.Simulation: simulationIndex :: Simulation Int
+ Simulation.Aivika.Simulation: simulationSpecs :: Simulation Specs
+ Simulation.Aivika.Simulation: throwSimulation :: IOException -> Simulation a
+ Simulation.Aivika.Specs: Euler :: Method
+ Simulation.Aivika.Specs: RungeKutta2 :: Method
+ Simulation.Aivika.Specs: RungeKutta4 :: Method
+ Simulation.Aivika.Specs: Specs :: Double -> Double -> Double -> Method -> Specs
+ Simulation.Aivika.Specs: basicTime :: Specs -> Int -> Int -> Double
+ Simulation.Aivika.Specs: data Method
+ Simulation.Aivika.Specs: data Specs
+ Simulation.Aivika.Specs: integIterationBnds :: Specs -> (Int, Int)
+ Simulation.Aivika.Specs: integIterationHiBnd :: Specs -> Int
+ Simulation.Aivika.Specs: integIterationLoBnd :: Specs -> Int
+ Simulation.Aivika.Specs: integPhaseBnds :: Specs -> (Int, Int)
+ Simulation.Aivika.Specs: integPhaseHiBnd :: Specs -> Int
+ Simulation.Aivika.Specs: integPhaseLoBnd :: Specs -> Int
+ Simulation.Aivika.Specs: integTimes :: Specs -> [Double]
+ Simulation.Aivika.Specs: spcDT :: Specs -> Double
+ Simulation.Aivika.Specs: spcMethod :: Specs -> Method
+ Simulation.Aivika.Specs: spcStartTime :: Specs -> Double
+ Simulation.Aivika.Specs: spcStopTime :: Specs -> Double
+ Simulation.Aivika.SystemDynamics: (./=.) :: Eq a => Dynamics a -> Dynamics a -> Dynamics Bool
+ Simulation.Aivika.SystemDynamics: (.<.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
+ Simulation.Aivika.SystemDynamics: (.<=.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
+ Simulation.Aivika.SystemDynamics: (.==.) :: Eq a => Dynamics a -> Dynamics a -> Dynamics Bool
+ Simulation.Aivika.SystemDynamics: (.>.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
+ Simulation.Aivika.SystemDynamics: (.>=.) :: Ord a => Dynamics a -> Dynamics a -> Dynamics Bool
+ Simulation.Aivika.SystemDynamics: delay :: Dynamics a -> Dynamics Double -> Dynamics a -> Dynamics a
+ Simulation.Aivika.SystemDynamics: delay1 :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: delay1I :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: delay3 :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: delay3I :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: delayN :: Dynamics Double -> Dynamics Double -> Int -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: delayNI :: Dynamics Double -> Dynamics Double -> Int -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: forecast :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: ifDynamics :: Dynamics Bool -> Dynamics a -> Dynamics a -> Dynamics a
+ Simulation.Aivika.SystemDynamics: integ :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: lookupDynamics :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double
+ Simulation.Aivika.SystemDynamics: lookupStepwiseDynamics :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double
+ Simulation.Aivika.SystemDynamics: maxDynamics :: Ord a => Dynamics a -> Dynamics a -> Dynamics a
+ Simulation.Aivika.SystemDynamics: minDynamics :: Ord a => Dynamics a -> Dynamics a -> Dynamics a
+ Simulation.Aivika.SystemDynamics: npv :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: npve :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: smooth :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: smooth3 :: Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: smooth3I :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: smoothI :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: smoothN :: Dynamics Double -> Dynamics Double -> Int -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: smoothNI :: Dynamics Double -> Dynamics Double -> Int -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.SystemDynamics: sumDynamics :: (Num a, Unboxed a) => Dynamics a -> Dynamics a -> Simulation (Dynamics a)
+ Simulation.Aivika.SystemDynamics: trend :: Dynamics Double -> Dynamics Double -> Dynamics Double -> Simulation (Dynamics Double)
+ Simulation.Aivika.Unboxed: class MArray IOUArray e IO => Unboxed e where newUnboxedArray_ = newArray_
+ Simulation.Aivika.Unboxed: instance Unboxed Bool
+ Simulation.Aivika.Unboxed: instance Unboxed Char
+ Simulation.Aivika.Unboxed: instance Unboxed Double
+ Simulation.Aivika.Unboxed: instance Unboxed Float
+ Simulation.Aivika.Unboxed: instance Unboxed Int
+ Simulation.Aivika.Unboxed: instance Unboxed Int16
+ Simulation.Aivika.Unboxed: instance Unboxed Int32
+ Simulation.Aivika.Unboxed: instance Unboxed Int64
+ Simulation.Aivika.Unboxed: instance Unboxed Int8
+ Simulation.Aivika.Unboxed: instance Unboxed Word
+ Simulation.Aivika.Unboxed: instance Unboxed Word16
+ Simulation.Aivika.Unboxed: instance Unboxed Word32
+ Simulation.Aivika.Unboxed: instance Unboxed Word64
+ Simulation.Aivika.Unboxed: instance Unboxed Word8
+ Simulation.Aivika.Unboxed: newUnboxedArray_ :: (Unboxed e, Ix i) => (i, i) -> IO (IOUArray i e)
+ Simulation.Aivika.Var: data Var a
+ Simulation.Aivika.Var: freezeVar :: Var a -> Event (Array Int Double, Array Int a)
+ Simulation.Aivika.Var: modifyVar :: Var a -> (a -> a) -> Event ()
+ Simulation.Aivika.Var: newVar :: a -> Simulation (Var a)
+ Simulation.Aivika.Var: readVar :: Var a -> Event a
+ Simulation.Aivika.Var: varChanged :: Var a -> Signal a
+ Simulation.Aivika.Var: varChanged_ :: Var a -> Signal ()
+ Simulation.Aivika.Var: writeVar :: Var a -> a -> Event ()
+ Simulation.Aivika.Var.Unboxed: data Var a
+ Simulation.Aivika.Var.Unboxed: freezeVar :: Unboxed a => Var a -> Event (Array Int Double, Array Int a)
+ Simulation.Aivika.Var.Unboxed: modifyVar :: Unboxed a => Var a -> (a -> a) -> Event ()
+ Simulation.Aivika.Var.Unboxed: newVar :: Unboxed a => a -> Simulation (Var a)
+ Simulation.Aivika.Var.Unboxed: readVar :: Unboxed a => Var a -> Event a
+ Simulation.Aivika.Var.Unboxed: varChanged :: Var a -> Signal a
+ Simulation.Aivika.Var.Unboxed: varChanged_ :: Var a -> Signal ()
+ Simulation.Aivika.Var.Unboxed: writeVar :: Unboxed a => Var a -> a -> Event ()
+ Simulation.Aivika.Vector: appendVector :: Vector a -> a -> IO ()
+ Simulation.Aivika.Vector: copyVector :: Vector a -> IO (Vector a)
+ Simulation.Aivika.Vector: data Vector a
+ Simulation.Aivika.Vector: freezeVector :: Vector a -> IO (Array Int a)
+ Simulation.Aivika.Vector: newVector :: IO (Vector a)
+ Simulation.Aivika.Vector: readVector :: Vector a -> Int -> IO a
+ Simulation.Aivika.Vector: vectorBinarySearch :: Ord a => Vector a -> a -> IO Int
+ Simulation.Aivika.Vector: vectorCount :: Vector a -> IO Int
+ Simulation.Aivika.Vector: vectorDeleteAt :: Vector a -> Int -> IO ()
+ Simulation.Aivika.Vector: vectorIndex :: Eq a => Vector a -> a -> IO Int
+ Simulation.Aivika.Vector: vectorInsert :: Vector a -> Int -> a -> IO ()
+ Simulation.Aivika.Vector: writeVector :: Vector a -> Int -> a -> IO ()
+ Simulation.Aivika.Vector.Unboxed: appendVector :: Unboxed a => Vector a -> a -> IO ()
+ Simulation.Aivika.Vector.Unboxed: copyVector :: Unboxed a => Vector a -> IO (Vector a)
+ Simulation.Aivika.Vector.Unboxed: data Vector a
+ Simulation.Aivika.Vector.Unboxed: freezeVector :: Unboxed a => Vector a -> IO (Array Int a)
+ Simulation.Aivika.Vector.Unboxed: newVector :: Unboxed a => IO (Vector a)
+ Simulation.Aivika.Vector.Unboxed: readVector :: Unboxed a => Vector a -> Int -> IO a
+ Simulation.Aivika.Vector.Unboxed: vectorBinarySearch :: (Unboxed a, Ord a) => Vector a -> a -> IO Int
+ Simulation.Aivika.Vector.Unboxed: vectorCount :: Unboxed a => Vector a -> IO Int
+ Simulation.Aivika.Vector.Unboxed: vectorDeleteAt :: Unboxed a => Vector a -> Int -> IO ()
+ Simulation.Aivika.Vector.Unboxed: vectorIndex :: (Unboxed a, Eq a) => Vector a -> a -> IO Int
+ Simulation.Aivika.Vector.Unboxed: vectorInsert :: Unboxed a => Vector a -> Int -> a -> IO ()
+ Simulation.Aivika.Vector.Unboxed: writeVector :: Unboxed a => Vector a -> Int -> a -> IO ()
- Simulation.Aivika.Queue: data Queue a
+ Simulation.Aivika.Queue: data Queue si qi sm qm so qo a
- Simulation.Aivika.Queue: dequeue :: Queue a -> IO ()
+ Simulation.Aivika.Queue: dequeue :: (DequeueStrategy si qi, DequeueStrategy sm qm, EnqueueStrategy so qo) => Queue si qi sm qm so qo a -> Process a
- Simulation.Aivika.Queue: enqueue :: Queue a -> a -> IO ()
+ Simulation.Aivika.Queue: enqueue :: (EnqueueStrategy si qi, EnqueueStrategy sm qm, DequeueStrategy so qo) => Queue si qi sm qm so qo a -> a -> Process ()
- Simulation.Aivika.Queue: newQueue :: IO (Queue a)
+ Simulation.Aivika.Queue: newQueue :: (QueueStrategy si qi, QueueStrategy sm qm, QueueStrategy so qo) => si -> sm -> so -> Int -> Simulation (Queue si qi sm qm so qo a)
- Simulation.Aivika.Queue: queueCount :: Queue a -> IO Int
+ Simulation.Aivika.Queue: queueCount :: Queue si qi sm qm so qo a -> Event Int
- Simulation.Aivika.Queue: queueNull :: Queue a -> IO Bool
+ Simulation.Aivika.Queue: queueNull :: Queue si qi sm qm so qo a -> Event Bool
Files
- Simulation/Aivika/Agent.hs +252/−0
- Simulation/Aivika/Cont.hs +18/−0
- Simulation/Aivika/DoubleLinkedList.hs +165/−0
- Simulation/Aivika/Dynamics.hs +13/−6
- Simulation/Aivika/Dynamics/Agent.hs +0/−343
- Simulation/Aivika/Dynamics/Base.hs +0/−46
- Simulation/Aivika/Dynamics/Buffer.hs +0/−189
- Simulation/Aivika/Dynamics/Cont.hs +0/−18
- Simulation/Aivika/Dynamics/EventQueue.hs +0/−215
- Simulation/Aivika/Dynamics/FIFO.hs +0/−207
- Simulation/Aivika/Dynamics/Fold.hs +82/−0
- Simulation/Aivika/Dynamics/Internal/Cont.hs +0/−304
- Simulation/Aivika/Dynamics/Internal/Dynamics.hs +0/−310
- Simulation/Aivika/Dynamics/Internal/Fold.hs +0/−92
- Simulation/Aivika/Dynamics/Internal/Interpolate.hs +0/−62
- Simulation/Aivika/Dynamics/Internal/Memo.hs +0/−196
- Simulation/Aivika/Dynamics/Internal/Process.hs +0/−300
- Simulation/Aivika/Dynamics/Internal/Signal.hs +0/−347
- Simulation/Aivika/Dynamics/Internal/Simulation.hs +0/−202
- Simulation/Aivika/Dynamics/Internal/Time.hs +0/−59
- Simulation/Aivika/Dynamics/Interpolate.hs +63/−0
- Simulation/Aivika/Dynamics/LIFO.hs +0/−196
- Simulation/Aivika/Dynamics/Memo.hs +125/−0
- Simulation/Aivika/Dynamics/Memo/Unboxed.hs +100/−0
- Simulation/Aivika/Dynamics/Parameter.hs +0/−83
- Simulation/Aivika/Dynamics/Process.hs +0/−42
- Simulation/Aivika/Dynamics/Random.hs +18/−54
- Simulation/Aivika/Dynamics/Ref.hs +0/−89
- Simulation/Aivika/Dynamics/Resource.hs +0/−171
- Simulation/Aivika/Dynamics/Signal.hs +0/−184
- Simulation/Aivika/Dynamics/Simulation.hs +0/−29
- Simulation/Aivika/Dynamics/SystemDynamics.hs +0/−788
- Simulation/Aivika/Dynamics/UVar.hs +0/−164
- Simulation/Aivika/Dynamics/Var.hs +0/−163
- Simulation/Aivika/Event.hs +36/−0
- Simulation/Aivika/Internal/Cont.hs +320/−0
- Simulation/Aivika/Internal/Dynamics.hs +217/−0
- Simulation/Aivika/Internal/Event.hs +349/−0
- Simulation/Aivika/Internal/Process.hs +325/−0
- Simulation/Aivika/Internal/Signal.hs +260/−0
- Simulation/Aivika/Internal/Simulation.hs +193/−0
- Simulation/Aivika/Internal/Specs.hs +199/−0
- Simulation/Aivika/Parameter.hs +59/−0
- Simulation/Aivika/Parameter/Random.hs +41/−0
- Simulation/Aivika/Process.hs +47/−0
- Simulation/Aivika/Queue.hs +294/−85
- Simulation/Aivika/QueueStrategy.hs +154/−0
- Simulation/Aivika/Random.hs +53/−0
- Simulation/Aivika/Ref.hs +69/−0
- Simulation/Aivika/Resource.hs +270/−0
- Simulation/Aivika/Signal.hs +137/−0
- Simulation/Aivika/Simulation.hs +27/−0
- Simulation/Aivika/Specs.hs +25/−0
- Simulation/Aivika/SystemDynamics.hs +620/−0
- Simulation/Aivika/UVector.hs +0/−188
- Simulation/Aivika/Unboxed.hs +43/−0
- Simulation/Aivika/Var.hs +158/−0
- Simulation/Aivika/Var/Unboxed.hs +158/−0
- Simulation/Aivika/Vector.hs +1/−0
- Simulation/Aivika/Vector/Unboxed.hs +186/−0
- aivika.cabal +44/−36
- examples/BassDiffusion.hs +18/−17
- examples/ChemicalReaction.hs +12/−15
- examples/ChemicalReactionRec.hs +0/−22
- examples/FishBank.hs +37/−43
- examples/FishBankRec.hs +0/−51
- examples/Furnace.hs +89/−117
- examples/MachRep1.hs +17/−17
- examples/MachRep1EventDriven.hs +19/−21
- examples/MachRep1TimeDriven.hs +21/−21
- examples/MachRep2.hs +24/−24
- examples/MachRep3.hs +27/−27
- examples/TimeOut.hs +27/−28
- examples/TimeOutInt.hs +21/−22
+ Simulation/Aivika/Agent.hs view
@@ -0,0 +1,252 @@++-- |+-- Module : Simulation.Aivika.Agent+-- Copyright : Copyright (c) 2009-2013, 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 introduces basic entities for the agent-based modeling.+--+module Simulation.Aivika.Agent+ (Agent,+ AgentState,+ newAgent,+ newState,+ newSubstate,+ agentState,+ agentStateChanged,+ agentStateChanged_,+ activateState,+ stateAgent,+ stateParent,+ addTimeout,+ addTimer,+ setStateActivation,+ setStateDeactivation,+ setStateTransition) where++import Data.IORef+import Control.Monad++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Signal++--+-- Agent-based Modeling+--++-- | Represents an agent.+data Agent = Agent { agentModeRef :: IORef AgentMode,+ agentStateRef :: IORef (Maybe AgentState), + agentStateChangedSource :: SignalSource (Maybe AgentState) }++-- | Represents the agent state.+data AgentState = AgentState { stateAgent :: Agent,+ -- ^ Return the corresponded agent.+ stateParent :: Maybe AgentState,+ -- ^ Return the parent state or 'Nothing'.+ stateActivateRef :: IORef (Event ()),+ stateDeactivateRef :: IORef (Event ()),+ stateTransitRef :: IORef (Event (Maybe AgentState)),+ stateVersionRef :: IORef Int }+ +data AgentMode = CreationMode+ | TransientMode+ | ProcessingMode+ +instance Eq Agent where+ x == y = agentStateRef x == agentStateRef y -- unique references+ +instance Eq AgentState where+ x == y = stateVersionRef x == stateVersionRef y -- unique references++fullPath :: AgentState -> [AgentState] -> [AgentState]+fullPath st acc =+ case stateParent st of+ Nothing -> st : acc+ Just st' -> fullPath st' (st : acc)++partitionPath :: [AgentState] -> [AgentState] -> ([AgentState], [AgentState])+partitionPath path1 path2 =+ case (path1, path2) of+ (h1 : t1, [h2]) | h1 == h2 -> + (reverse path1, path2)+ (h1 : t1, h2 : t2) | h1 == h2 -> + partitionPath t1 t2+ _ ->+ (reverse path1, path2)++findPath :: Maybe AgentState -> AgentState -> ([AgentState], [AgentState])+findPath Nothing target = ([], fullPath target [])+findPath (Just source) target+ | stateAgent source /= stateAgent target =+ error "Different agents: findPath."+ | otherwise =+ partitionPath path1 path2+ where+ path1 = fullPath source []+ path2 = fullPath target []++traversePath :: Maybe AgentState -> AgentState -> Event ()+traversePath source target =+ let (path1, path2) = findPath source target+ agent = stateAgent target+ activate st p = invokeEvent p =<< readIORef (stateActivateRef st)+ deactivate st p = invokeEvent p =<< readIORef (stateDeactivateRef st)+ transit st p = invokeEvent p =<< readIORef (stateTransitRef st)+ continue st p = invokeEvent p $ traversePath (Just target) st+ in Event $ \p ->+ unless (null path1 && null path2) $+ do writeIORef (agentModeRef agent) TransientMode+ forM_ path1 $ \st ->+ do writeIORef (agentStateRef agent) (Just st)+ deactivate st p+ -- it makes all timeout and timer handlers outdated+ modifyIORef (stateVersionRef st) (1 +)+ forM_ path2 $ \st ->+ do writeIORef (agentStateRef agent) (Just st)+ activate st p+ st' <- transit target p+ case st' of+ Nothing ->+ do writeIORef (agentModeRef agent) ProcessingMode+ triggerAgentStateChanged p agent+ Just st' ->+ continue st' p++-- | Add to the state a timeout handler that will be actuated +-- in the specified time period, while the state remains active.+addTimeout :: AgentState -> Double -> Event () -> Event ()+addTimeout st dt action =+ Event $ \p ->+ do v <- readIORef (stateVersionRef st)+ let m1 = Event $ \p ->+ do v' <- readIORef (stateVersionRef st)+ when (v == v') $+ invokeEvent p action+ m2 = enqueueEvent (pointTime p + dt) m1+ invokeEvent p m2++-- | Add to the state a timer handler that will be actuated+-- in the specified time period and then repeated again many times,+-- while the state remains active.+addTimer :: AgentState -> Event Double -> Event () -> Event ()+addTimer st dt action =+ Event $ \p ->+ do v <- readIORef (stateVersionRef st)+ let m1 = Event $ \p ->+ do v' <- readIORef (stateVersionRef st)+ when (v == v') $+ do invokeEvent p m2+ invokeEvent p action+ m2 = Event $ \p ->+ do dt' <- invokeEvent p dt+ invokeEvent p $ enqueueEvent (pointTime p + dt') m1+ invokeEvent p m2++-- | Create a new state.+newState :: Agent -> Simulation AgentState+newState agent =+ Simulation $ \r ->+ do aref <- newIORef $ return ()+ dref <- newIORef $ return ()+ tref <- newIORef $ return Nothing+ vref <- newIORef 0+ return AgentState { stateAgent = agent,+ stateParent = Nothing,+ stateActivateRef = aref,+ stateDeactivateRef = dref,+ stateTransitRef = tref,+ stateVersionRef = vref }++-- | Create a child state.+newSubstate :: AgentState -> Simulation AgentState+newSubstate parent =+ Simulation $ \r ->+ do let agent = stateAgent parent + aref <- newIORef $ return ()+ dref <- newIORef $ return ()+ tref <- newIORef $ return Nothing+ vref <- newIORef 0+ return AgentState { stateAgent = agent,+ stateParent = Just parent,+ stateActivateRef= aref,+ stateDeactivateRef = dref,+ stateTransitRef = tref,+ stateVersionRef = vref }++-- | Create an agent.+newAgent :: Simulation Agent+newAgent =+ Simulation $ \r ->+ do modeRef <- newIORef CreationMode+ stateRef <- newIORef Nothing+ stateChangedSource <- invokeSimulation r newSignalSource+ return Agent { agentModeRef = modeRef,+ agentStateRef = stateRef, + agentStateChangedSource = stateChangedSource }++-- | Return the selected downmost active state.+agentState :: Agent -> Event (Maybe AgentState)+agentState agent =+ Event $ \p -> readIORef (agentStateRef agent)+ +-- | Select the next downmost active state. The activation is repeated while+-- there is the transition state defined by 'setStateTransition'.+activateState :: AgentState -> Event ()+activateState st =+ Event $ \p ->+ do let agent = stateAgent st+ mode <- readIORef (agentModeRef agent)+ case mode of+ CreationMode ->+ do x0 <- readIORef (agentStateRef agent)+ invokeEvent p $ traversePath x0 st+ TransientMode ->+ error $+ "Use the setStateTransition function to define " +++ "the transition state: activateState."+ ProcessingMode ->+ do x0 @ (Just st0) <- readIORef (agentStateRef agent)+ invokeEvent p $ traversePath x0 st++-- | Set the activation computation for the specified state.+setStateActivation :: AgentState -> Event () -> Simulation ()+setStateActivation st action =+ Simulation $ \r ->+ writeIORef (stateActivateRef st) action+ +-- | Set the deactivation computation for the specified state.+setStateDeactivation :: AgentState -> Event () -> Simulation ()+setStateDeactivation st action =+ Simulation $ \r ->+ writeIORef (stateDeactivateRef st) action+ +-- | Set the transition state which will be next and which is used only+-- when activating the state directly with help of 'activateState'.+-- If the state was activated intermediately, when activating directly+-- another state, then this computation is not used.+setStateTransition :: AgentState -> Event (Maybe AgentState) -> Simulation ()+setStateTransition st action =+ Simulation $ \r ->+ writeIORef (stateTransitRef st) action+ +-- | Trigger the signal when the agent state changes.+triggerAgentStateChanged :: Point -> Agent -> IO ()+triggerAgentStateChanged p agent =+ do st <- readIORef (agentStateRef agent)+ invokeEvent p $ triggerSignal (agentStateChangedSource agent) st++-- | Return a signal that notifies about every change of the state.+agentStateChanged :: Agent -> Signal (Maybe AgentState)+agentStateChanged agent =+ publishSignal (agentStateChangedSource agent)++-- | Return a signal that notifies about every change of the state.+agentStateChanged_ :: Agent -> Signal ()+agentStateChanged_ agent =+ mapSignal (const ()) $ agentStateChanged agent
+ Simulation/Aivika/Cont.hs view
@@ -0,0 +1,18 @@++-- |+-- Module : Simulation.Aivika.Cont+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The 'Cont' monad is a variation of the standard Cont monad +-- and F# async workflow, where the result of applying +-- the continuations is the 'Event' computation.+--+module Simulation.Aivika.Cont+ (Cont) where++import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Cont
+ Simulation/Aivika/DoubleLinkedList.hs view
@@ -0,0 +1,165 @@++-- |+-- Module : Simulation.Aivika.DoubleLinkedList+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- An imperative double-linked list.+--+module Simulation.Aivika.DoubleLinkedList + (DoubleLinkedList, + listNull, + listCount,+ newList, + listInsertFirst,+ listAddLast,+ listRemoveFirst,+ listRemoveLast,+ listFirst,+ listLast) where ++import Data.IORef+import Control.Monad++-- | A cell of the double-linked list.+data DoubleLinkedItem a = + DoubleLinkedItem { itemVal :: a,+ itemPrev :: IORef (Maybe (DoubleLinkedItem a)),+ itemNext :: IORef (Maybe (DoubleLinkedItem a)) }+ +-- | The 'DoubleLinkedList' type represents an imperative double-linked list.+data DoubleLinkedList a = + DoubleLinkedList { listHead :: IORef (Maybe (DoubleLinkedItem a)),+ listTail :: IORef (Maybe (DoubleLinkedItem a)), + listSize :: IORef Int }++-- | Test whether the list is empty.+listNull :: DoubleLinkedList a -> IO Bool+listNull x =+ do head <- readIORef (listHead x) + case head of+ Nothing -> return True+ Just _ -> return False+ +-- | Return the number of elements in the list.+listCount :: DoubleLinkedList a -> IO Int+listCount x = readIORef (listSize x)++-- | Create a new list.+newList :: IO (DoubleLinkedList a)+newList =+ do head <- newIORef Nothing + tail <- newIORef Nothing+ size <- newIORef 0+ return DoubleLinkedList { listHead = head,+ listTail = tail,+ listSize = size }++-- | Insert a new element in the beginning.+listInsertFirst :: DoubleLinkedList a -> a -> IO ()+listInsertFirst x v =+ do size <- readIORef (listSize x)+ writeIORef (listSize x) (size + 1)+ head <- readIORef (listHead x)+ case head of+ Nothing ->+ do prev <- newIORef Nothing+ next <- newIORef Nothing+ let item = Just DoubleLinkedItem { itemVal = v, + itemPrev = prev, + itemNext = next }+ writeIORef (listHead x) item+ writeIORef (listTail x) item+ Just h ->+ do prev <- newIORef Nothing+ next <- newIORef head+ let item = Just DoubleLinkedItem { itemVal = v,+ itemPrev = prev,+ itemNext = next }+ writeIORef (itemPrev h) item+ writeIORef (listHead x) item++-- | Add a new element to the end.+listAddLast :: DoubleLinkedList a -> a -> IO ()+listAddLast x v =+ do size <- readIORef (listSize x)+ writeIORef (listSize x) (size + 1)+ tail <- readIORef (listTail x)+ case tail of+ Nothing ->+ do prev <- newIORef Nothing+ next <- newIORef Nothing+ let item = Just DoubleLinkedItem { itemVal = v, + itemPrev = prev, + itemNext = next }+ writeIORef (listHead x) item+ writeIORef (listTail x) item+ Just t ->+ do prev <- newIORef tail+ next <- newIORef Nothing+ let item = Just DoubleLinkedItem { itemVal = v,+ itemPrev = prev,+ itemNext = next }+ writeIORef (itemNext t) item+ writeIORef (listTail x) item++-- | Remove the first element.+listRemoveFirst :: DoubleLinkedList a -> IO ()+listRemoveFirst x =+ do head <- readIORef (listHead x) + case head of+ Nothing ->+ error "Empty list: listRemoveFirst"+ Just h ->+ do size <- readIORef (listSize x)+ writeIORef (listSize x) (size - 1)+ head' <- readIORef (itemNext h)+ case head' of+ Nothing ->+ do writeIORef (listHead x) Nothing+ writeIORef (listTail x) Nothing+ Just h' ->+ do writeIORef (itemPrev h') Nothing+ writeIORef (listHead x) head'++-- | Remove the last element.+listRemoveLast :: DoubleLinkedList a -> IO ()+listRemoveLast x =+ do tail <- readIORef (listTail x) + case tail of+ Nothing ->+ error "Empty list: listRemoveLast"+ Just t ->+ do size <- readIORef (listSize x)+ writeIORef (listSize x) (size - 1)+ tail' <- readIORef (itemPrev t)+ case tail' of+ Nothing ->+ do writeIORef (listHead x) Nothing+ writeIORef (listTail x) Nothing+ Just t' ->+ do writeIORef (itemNext t') Nothing+ writeIORef (listTail x) tail'++-- | Return the first element.+listFirst :: DoubleLinkedList a -> IO a+listFirst x =+ do head <- readIORef (listHead x)+ case head of+ Nothing ->+ error "Empty list: listFirst"+ Just h ->+ return $ itemVal h++-- | Return the last element.+listLast :: DoubleLinkedList a -> IO a+listLast x =+ do tail <- readIORef (listTail x)+ case tail of+ Nothing ->+ error "Empty list: listLast"+ Just t ->+ return $ itemVal t
Simulation/Aivika/Dynamics.hs view
@@ -7,11 +7,10 @@ -- Stability : experimental -- Tested with: GHC 7.6.3 ----- The module defines the 'Dynamics' monad representing an abstract dynamic --- process, i.e. a time varying polymorphic function. +-- The module defines the 'Dynamics' monad representing a time varying polymorphic function. ---module Simulation.Aivika.Dynamics - (-- * Dynamics+module Simulation.Aivika.Dynamics+ (-- * Dynamics Monad Dynamics, DynamicsLift(..), runDynamicsInStartTime,@@ -22,6 +21,14 @@ -- * Error Handling catchDynamics, finallyDynamics,- throwDynamics) where+ throwDynamics,+ -- * Time parameters+ starttime,+ stoptime,+ dt,+ time,+ isTimeInteg,+ integIteration,+ integPhase) where -import Simulation.Aivika.Dynamics.Internal.Dynamics+import Simulation.Aivika.Internal.Dynamics
− Simulation/Aivika/Dynamics/Agent.hs
@@ -1,343 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Agent--- Copyright : Copyright (c) 2009-2013, 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 introduces basic entities for the agent-based modeling.------ WARNING: the module is not well tested. This caution is related mainly to--- managing the nested states.--- --- At the same time, the timer and timeout handlers seem to be well tested as--- they are just light-weight wrappers creating the event handlers that are--- already processed by the event queue.-----module Simulation.Aivika.Dynamics.Agent- (Agent,- AgentState,- newAgent,- newState,- newSubstate,- agentQueue,- agentState,- agentStateChanged,- agentStateChanged_,- activateState,- initState,- stateAgent,- stateParent,- addTimeout,- addTimer,- stateActivation,- stateDeactivation,- setStateActivation,- setStateDeactivation,- setStateTransition) where--import Data.IORef-import Control.Monad--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Signal------- Agent-based Modeling------- | Represents an agent.-data Agent = Agent { agentQueue :: EventQueue,- -- ^ Return the bound event queue.- agentModeRef :: IORef AgentMode,- agentStateRef :: IORef (Maybe AgentState), - agentStateChangedSource :: SignalSource (Maybe AgentState), - agentStateUpdatedSource :: SignalSource (Maybe AgentState) }---- | Represents the agent state.-data AgentState = AgentState { stateAgent :: Agent,- -- ^ Return the corresponded agent.- stateParent :: Maybe AgentState,- -- ^ Return the parent state or 'Nothing'.- stateActivateRef :: IORef (Dynamics ()),- stateDeactivateRef :: IORef (Dynamics ()),- stateTransitRef :: IORef (Dynamics (Maybe AgentState)),- stateVersionRef :: IORef Int }- -data AgentMode = CreationMode- | InitialMode- | TransientMode- | ProcessingMode- -instance Eq Agent where- x == y = agentStateRef x == agentStateRef y -- unique references- -instance Eq AgentState where- x == y = stateVersionRef x == stateVersionRef y -- unique references--fullPath :: AgentState -> [AgentState] -> [AgentState]-fullPath st acc =- case stateParent st of- Nothing -> st : acc- Just st' -> fullPath st' (st : acc)--partitionPath :: [AgentState] -> [AgentState] -> ([AgentState], [AgentState])-partitionPath path1 path2 =- case (path1, path2) of- (h1 : t1, [h2]) | h1 == h2 -> - (reverse path1, path2)- (h1 : t1, h2 : t2) | h1 == h2 -> - partitionPath t1 t2- _ ->- (reverse path1, path2)--findPath :: Maybe AgentState -> AgentState -> ([AgentState], [AgentState])-findPath Nothing target = ([], fullPath target [])-findPath (Just source) target- | stateAgent source /= stateAgent target =- error "Different agents: findPath."- | otherwise =- partitionPath path1 path2- where- path1 = fullPath source []- path2 = fullPath target []--traversePath :: Maybe AgentState -> AgentState -> Dynamics ()-traversePath source target =- let (path1, path2) = findPath source target- agent = stateAgent target- activate st p =- do Dynamics m <- readIORef (stateActivateRef st)- m p- deactivate st p =- do Dynamics m <- readIORef (stateDeactivateRef st)- m p- transit st p =- do Dynamics m <- readIORef (stateTransitRef st)- m p- continue st p =- do let Dynamics m = traversePath (Just target) st- m p- in Dynamics $ \p ->- unless (null path1 && null path2) $- do writeIORef (agentModeRef agent) TransientMode- forM_ path1 $ \st ->- do writeIORef (agentStateRef agent) (Just st)- deactivate st p- -- it makes all timeout and timer handlers outdated- modifyIORef (stateVersionRef st) (1 +)- forM_ path2 $ \st ->- do when (st == target) $- writeIORef (agentModeRef agent) InitialMode- writeIORef (agentStateRef agent) (Just st)- activate st p- writeIORef (agentModeRef agent) TransientMode - st' <- transit target p- case st' of- Nothing ->- do writeIORef (agentModeRef agent) ProcessingMode- triggerAgentStateChanged p agent- Just st' ->- continue st' p---- | Add to the state a timeout handler that will be actuated --- in the specified time period, while the state remains active.-addTimeout :: AgentState -> Double -> Dynamics () -> Dynamics ()-addTimeout st dt (Dynamics action) =- Dynamics $ \p ->- do let q = agentQueue (stateAgent st)- Dynamics m0 = runQueueSync q- m0 p -- ensure that the agent state is actual- v <- readIORef (stateVersionRef st)- let m1 = Dynamics $ \p ->- do -- checkTime p (stateAgent st) "addTimeout"- v' <- readIORef (stateVersionRef st)- when (v == v') $ action p- Dynamics m2 = enqueue q (pointTime p + dt) m1- m2 p---- | Add to the state a timer handler that will be actuated--- in the specified time period and then repeated again many times,--- while the state remains active.-addTimer :: AgentState -> Dynamics Double -> Dynamics () -> Dynamics ()-addTimer st (Dynamics dt) (Dynamics action) =- Dynamics $ \p ->- do let q = agentQueue (stateAgent st)- Dynamics m0 = runQueueSync q- m0 p -- ensure that the agent state is actual- v <- readIORef (stateVersionRef st)- let m1 = Dynamics $ \p ->- do -- checkTime p (stateAgent st) "addTimer"- v' <- readIORef (stateVersionRef st)- when (v == v') $ do { m2 p; action p }- Dynamics m2 = - Dynamics $ \p ->- do dt' <- dt p- let Dynamics m3 = enqueue q (pointTime p + dt') m1- m3 p- m2 p---- | Create a new state.-newState :: Agent -> Simulation AgentState-newState agent =- Simulation $ \r ->- do aref <- newIORef $ return ()- dref <- newIORef $ return ()- tref <- newIORef $ return Nothing- vref <- newIORef 0- return AgentState { stateAgent = agent,- stateParent = Nothing,- stateActivateRef = aref,- stateDeactivateRef = dref,- stateTransitRef = tref,- stateVersionRef = vref }---- | Create a child state.-newSubstate :: AgentState -> Simulation AgentState-newSubstate parent =- Simulation $ \r ->- do let agent = stateAgent parent - aref <- newIORef $ return ()- dref <- newIORef $ return ()- tref <- newIORef $ return Nothing- vref <- newIORef 0- return AgentState { stateAgent = agent,- stateParent = Just parent,- stateActivateRef= aref,- stateDeactivateRef = dref,- stateTransitRef = tref,- stateVersionRef = vref }---- | Create an agent bound with the specified event queue.-newAgent :: EventQueue -> Simulation Agent-newAgent queue =- Simulation $ \r ->- do modeRef <- newIORef CreationMode- stateRef <- newIORef Nothing- let Simulation m1 = newSignalSourceUnsafe- Simulation m2 = newSignalSource queue- stateChangedSource <- m1 r- stateUpdatedSource <- m2 r- return Agent { agentQueue = queue,- agentModeRef = modeRef,- agentStateRef = stateRef, - agentStateChangedSource = stateChangedSource, - agentStateUpdatedSource = stateUpdatedSource }---- | Return the selected downmost active state.-agentState :: Agent -> Dynamics (Maybe AgentState)-agentState agent =- Dynamics $ \p -> - do let Dynamics m = runQueueSync $ agentQueue agent - m p -- ensure that the agent state is actual- readIORef (agentStateRef agent)- --- | Select the next downmost active state. The activation is repeated while--- there is the transition state defined by 'setStateTransition'.-activateState :: AgentState -> Dynamics ()-activateState st =- Dynamics $ \p ->- do let agent = stateAgent st- Dynamics m = runQueueSync $ agentQueue agent - m p -- ensure that the agent state is actual- mode <- readIORef (agentModeRef agent)- case mode of- CreationMode ->- do x0 <- readIORef (agentStateRef agent)- let Dynamics m = traversePath x0 st- m p- InitialMode ->- error $ - "Use the setStateTransition function to define " ++- "the transition state: activateState."- TransientMode ->- error $- "Use the setStateTransition function to define " ++- "the transition state: activateState."- ProcessingMode ->- do x0 @ (Just st0) <- readIORef (agentStateRef agent)- let Dynamics m = traversePath x0 st- m p--{-# DEPRECATED initState "Rewrite using the setStateTransition function instead." #-}- --- | Activate the child state during the direct activation of --- the parent state. This call is ignored in other cases.-initState :: AgentState -> Dynamics ()-initState st =- Dynamics $ \p ->- do let agent = stateAgent st- Dynamics m = runQueueSync $ agentQueue agent - m p -- ensure that the agent state is actual- mode <- readIORef (agentModeRef agent)- case mode of- CreationMode ->- error $- "To run the agent for the fist time, use " ++- "the activateState function: initState."- InitialMode ->- do x0 @ (Just st0) <- readIORef (agentStateRef agent)- let Dynamics m = traversePath x0 st- m p- TransientMode -> - return ()- ProcessingMode ->- error $- "Use the activateState function everywhere outside " ++- "the state activation: initState."--{-# DEPRECATED stateActivation "Use the setStateActivation function instead" #-}-{-# DEPRECATED stateDeactivation "Use the setStateDeactivation function instead" #-}---- | Set the activation computation for the specified state.-stateActivation :: AgentState -> Dynamics () -> Simulation ()-stateActivation = setStateActivation- --- | Set the deactivation computation for the specified state.-stateDeactivation :: AgentState -> Dynamics () -> Simulation ()-stateDeactivation = setStateDeactivation- --- | Set the activation computation for the specified state.-setStateActivation :: AgentState -> Dynamics () -> Simulation ()-setStateActivation st action =- Simulation $ \r ->- writeIORef (stateActivateRef st) action- --- | Set the deactivation computation for the specified state.-setStateDeactivation :: AgentState -> Dynamics () -> Simulation ()-setStateDeactivation st action =- Simulation $ \r ->- writeIORef (stateDeactivateRef st) action- --- | Set the transition state which will be next and which is used only--- when activating the state directly with help of 'activateState'.--- If the state was activated intermediately, when activating directly--- another state, then this computation is not used.-setStateTransition :: AgentState -> Dynamics (Maybe AgentState) -> Simulation ()-setStateTransition st action =- Simulation $ \r ->- writeIORef (stateTransitRef st) action- --- | Trigger the signal when the agent state changes.-triggerAgentStateChanged :: Point -> Agent -> IO ()-triggerAgentStateChanged p agent =- do st <- readIORef (agentStateRef agent)- let Dynamics m = triggerSignal (agentStateChangedSource agent) st- m p---- | Return a signal that notifies about every change of the state.-agentStateChanged :: Agent -> Signal (Maybe AgentState)-agentStateChanged v = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (agentStateUpdatedSource v)- m2 = publishSignal (agentStateChangedSource v)---- | Return a signal that notifies about every change of the state.-agentStateChanged_ :: Agent -> Signal ()-agentStateChanged_ agent =- mapSignal (const ()) $ agentStateChanged agent
− Simulation/Aivika/Dynamics/Base.hs
@@ -1,46 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Base--- Copyright : Copyright (c) 2009-2013, 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 basic functions for the 'Dynamics' monad.-----module Simulation.Aivika.Dynamics.Base- (-- * Time Parameters- starttime, - stoptime,- dt,- time,- integTimes,- isTimeInteg,- integIteration,- integIterationBnds,- integIterationLoBnd,- integIterationHiBnd,- -- * Interpolation and Initial Value- initDynamics,- discrete,- interpolate,- -- * Memoization- memo,- umemo,- memo0,- umemo0,- -- * Iterating- iterateDynamics,- -- * Fold- foldDynamics1,- foldDynamics,- -- * Norming- divideDynamics) where--import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Time-import Simulation.Aivika.Dynamics.Internal.Interpolate-import Simulation.Aivika.Dynamics.Internal.Memo-import Simulation.Aivika.Dynamics.Internal.Fold
− Simulation/Aivika/Dynamics/Buffer.hs
@@ -1,189 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Buffer--- Copyright : Copyright (c) 2009-2013, 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 limited queue similar to 'LIFO' and 'FIFO' but where --- the items are not represented. We know only of their number in the buffer and --- how many items were lost.----module Simulation.Aivika.Dynamics.Buffer- (Buffer,- bufferQueue,- bufferNull,- bufferFull,- bufferMaxCount,- bufferCount,- bufferLostCount,- bufferEnqueue,- bufferDequeue,- bufferEnqueueLost,- newBuffer,- dequeueBuffer,- tryDequeueBuffer,- enqueueBuffer,- tryEnqueueBuffer,- enqueueBufferOrLost) where--import Data.IORef--import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Process-import Simulation.Aivika.Dynamics.Resource-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Signal--import Simulation.Aivika.Dynamics.LIFO-import Simulation.Aivika.Dynamics.FIFO---- | Represents the limited queue similar to 'LIFO' and 'FIFO' but where the items are not repsented.--- So, there is no order of items but their number is strictly limited.-data Buffer =- Buffer { bufferQueue :: EventQueue, -- ^ Return the event queue.- bufferMaxCount :: Int, -- ^ The maximum available number of items.- bufferReadRes :: Resource,- bufferWriteRes :: Resource,- bufferCountRef :: IORef Int,- bufferLostCountRef :: IORef Int, - bufferEnqueueSource :: SignalSource (),- bufferEnqueueLostSource :: SignalSource (),- bufferDequeueSource :: SignalSource (),- bufferUpdatedSource :: SignalSource () }- --- | Create a new queue with the specified maximum available number of items. -newBuffer :: EventQueue -> Int -> Simulation Buffer -newBuffer q count =- do i <- liftIO $ newIORef 0- l <- liftIO $ newIORef 0- r <- newResourceWithCount q count 0- w <- newResourceWithCount q count count- s1 <- newSignalSourceUnsafe- s2 <- newSignalSourceUnsafe- s3 <- newSignalSourceUnsafe- s4 <- newSignalSource q- return Buffer { bufferQueue = q,- bufferMaxCount = count,- bufferReadRes = r,- bufferWriteRes = w,- bufferCountRef = i,- bufferLostCountRef = l, - bufferEnqueueSource = s1,- bufferEnqueueLostSource = s2,- bufferDequeueSource = s3,- bufferUpdatedSource = s4 }- --- | Test whether the queue is empty.-bufferNull :: Buffer -> Dynamics Bool-bufferNull q =- do a <- bufferCount q- return (a == 0)---- | Test whether the queue is full.-bufferFull :: Buffer -> Dynamics Bool-bufferFull q =- do a <- bufferCount q- return (a == bufferMaxCount q)---- | Return the queue size.-bufferCount :: Buffer -> Dynamics Int-bufferCount q =- liftIO $ readIORef (bufferCountRef q)- --- | Return the number of lost items.-bufferLostCount :: Buffer -> Dynamics Int-bufferLostCount q =- liftIO $ readIORef (bufferLostCountRef q)- --- | Dequeue suspending the process if the buffer is empty.-dequeueBuffer :: Buffer -> Process ()-dequeueBuffer q =- do requestResource (bufferReadRes q)- liftIO $ dequeueImpl q- releaseResource (bufferWriteRes q)- liftDynamics $ triggerSignal (bufferDequeueSource q) ()- --- | Try to dequeue immediately. -tryDequeueBuffer :: Buffer -> Dynamics Bool-tryDequeueBuffer q =- do x <- tryRequestResourceInDynamics (bufferReadRes q)- if x - then do liftIO $ dequeueImpl q- releaseResourceInDynamics (bufferWriteRes q)- triggerSignal (bufferDequeueSource q) ()- return True- else return False---- | Enqueue the item suspending the process --- if the buffer is full. -enqueueBuffer :: Buffer -> Process ()-enqueueBuffer q =- do requestResource (bufferWriteRes q)- liftIO $ enqueueImpl q- releaseResource (bufferReadRes q)- liftDynamics $ triggerSignal (bufferEnqueueSource q) ()- --- | Try to enqueue the item immediately. -tryEnqueueBuffer :: Buffer -> Dynamics Bool-tryEnqueueBuffer q =- do x <- tryRequestResourceInDynamics (bufferWriteRes q)- if x - then do liftIO $ enqueueImpl q- releaseResourceInDynamics (bufferReadRes q)- triggerSignal (bufferEnqueueSource q) ()- return True- else return False---- | Try to enqueue the item. If the buffer is full--- then the item will be lost.-enqueueBufferOrLost :: Buffer -> Dynamics ()-enqueueBufferOrLost q =- do x <- tryRequestResourceInDynamics (bufferWriteRes q)- if x- then do liftIO $ enqueueImpl q- releaseResourceInDynamics (bufferReadRes q)- triggerSignal (bufferEnqueueSource q) ()- else do liftIO $ modifyIORef (bufferLostCountRef q) $ (+) 1- triggerSignal (bufferEnqueueLostSource q) ()---- | Return a signal that notifies when any item is enqueued.-bufferEnqueue :: Buffer -> Signal ()-bufferEnqueue q = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (bufferUpdatedSource q)- m2 = publishSignal (bufferEnqueueSource q)---- | Return a signal which notifies that the item was lost when--- attempting to add it to the full queue with help of --- 'enqueueBufferOrLost'.-bufferEnqueueLost :: Buffer -> Signal ()-bufferEnqueueLost q = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (bufferUpdatedSource q)- m2 = publishSignal (bufferEnqueueLostSource q)---- | Return a signal that notifies when any item is dequeued.-bufferDequeue :: Buffer -> Signal ()-bufferDequeue q = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (bufferUpdatedSource q)- m2 = publishSignal (bufferDequeueSource q)---- | An implementation method.-dequeueImpl :: Buffer -> IO ()-dequeueImpl q =- do i <- readIORef (bufferCountRef q)- let i' = i - 1- i' `seq` writeIORef (bufferCountRef q) i'---- | An implementation method.-enqueueImpl :: Buffer -> IO ()-enqueueImpl q =- do i <- readIORef (bufferCountRef q)- let i' = i + 1- i' `seq` writeIORef (bufferCountRef q) i'
− Simulation/Aivika/Dynamics/Cont.hs
@@ -1,18 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Cont--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ The 'Cont' monad is a variation of the standard Cont monad --- and F# async workflow, where the result of applying --- the continuation is a dynamic process.----module Simulation.Aivika.Dynamics.Cont- (Cont) where--import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Cont
− Simulation/Aivika/Dynamics/EventQueue.hs
@@ -1,215 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.EventQueue--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ The module introduces the event queue. An event handler is--- the Dynamics computation that has a single purpose to perform--- some side effect at the desired time. To pass in any message--- to the event, you can use a closure.----module Simulation.Aivika.Dynamics.EventQueue- (EventQueue,- newQueue,- enqueue,- enqueueWithTimes,- enqueueWithIntegTimes,- enqueueWithStartTime,- enqueueWithStopTime,- enqueueWithCurrentTime,- runQueue,- runQueueSync,- runQueueBefore,- runQueueSyncBefore,- queueCount) where--import Data.IORef-import Control.Monad--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import qualified Simulation.Aivika.PriorityQueue as PQ---- | The 'EventQueue' type represents the event queue.-data EventQueue = EventQueue { - queuePQ :: PQ.PriorityQueue (Dynamics ()),- queueBusy :: IORef Bool,- queueTime :: IORef Double, - -- Optimization- runQueue :: Dynamics (),- -- ^ Run the event queue processing its events.- -- There is no restiction on the time of the queue itself. It this time- -- is greater than the current simulation time then nothing happens.- runQueueSync :: Dynamics (),- -- ^ Run the event queue synchronously, i.e. the current time cannot be- -- less than the actual time of the queue itself.- --- -- You will rarely need to run the event queue explicitly, but- -- if you do want then this function is probably that one you should use.- runQueueBefore :: Dynamics (),- -- ^ Run the event queue processing only those events- -- which time is less than the current simulation time.- -- There is no restiction on the time of the queue itself. It this time- -- is greater than the current simulation time then nothing happens.- runQueueSyncBefore :: Dynamics ()- -- ^ Run the event queue synchronously processing only those events- -- which time is less than the current simulation time. But the current- -- time cannot be less than the actual time of the queue itself.- --- -- This function is usually called before a handler is subscribed- -- to the signal. Earlier 'runQueueSync' was called instead, which could- -- lead to the lost of the signal by the handler at time of direct- -- subscribing. Changed in version 0.6.1.- }---- | Create a new event queue.-newQueue :: Simulation EventQueue-newQueue = - Simulation $ \r ->- do let sc = runSpecs r- f <- newIORef False- t <- newIORef $ spcStartTime sc- pq <- PQ.newQueue- let q = EventQueue { queuePQ = pq,- queueBusy = f,- queueTime = t, - runQueue = runQueueCore True q,- runQueueSync = runQueueSyncCore True q,- runQueueBefore = runQueueCore False q,- runQueueSyncBefore = runQueueSyncCore False q }- return q- --- | Enqueue the event which must be actuated at the specified time.-enqueue :: EventQueue -> Double -> Dynamics () -> Dynamics ()-enqueue q t c = Dynamics r where- r p = let pq = queuePQ q in PQ.enqueue pq t c- --- | Run the event queue processing its events.-runQueueCore :: Bool -> EventQueue -> Dynamics ()-runQueueCore includingCurrentTime q = Dynamics r where- r p =- do let f = queueBusy q- f' <- readIORef f- unless f' $- do writeIORef f True- call q p- writeIORef f False- call q p =- do let pq = queuePQ q- f <- PQ.queueNull pq- unless f $- do (t2, c2) <- PQ.queueFront pq- let t = queueTime q- t' <- readIORef t- when (t2 < t') $ - error "The time value is too small: runQueueCore"- when ((t2 < pointTime p) ||- (includingCurrentTime && (t2 == pointTime p))) $- do writeIORef t t2- PQ.dequeue pq- let sc = pointSpecs p- t0 = spcStartTime sc- dt = spcDT sc- n2 = fromIntegral $ floor ((t2 - t0) / dt)- Dynamics k = c2- k $ p { pointTime = t2,- pointIteration = n2,- pointPhase = -1 }- call q p---- | Run the event queue synchronously, i.e. without past.-runQueueSyncCore :: Bool -> EventQueue -> Dynamics ()-runQueueSyncCore includingCurrentTime q = Dynamics r where- r p =- do let t = queueTime q- t' <- readIORef t- if pointTime p < t'- then error $- "The current time is less than " ++- "the time in the queue: runQueueSyncCore"- else m p- Dynamics m = if includingCurrentTime- then runQueue q- else runQueueBefore q- --- | Return the number of pending events that should--- be yet actuated.-queueCount :: EventQueue -> Dynamics Int-queueCount q = Dynamics r where- r p = - do let Dynamics m = runQueueSync q- m p- PQ.queueCount $ queuePQ q- --- | Actuate the event handler in the specified time points.-enqueueWithTimes :: EventQueue -> [Double] -> Dynamics () -> Dynamics ()-enqueueWithTimes q ts m = loop ts- where loop [] = return ()- loop (t : ts) = enqueue q t $ m >> loop ts- --- | Actuate the event handler in the specified time points.-enqueueWithPoints :: EventQueue -> [Point] -> Dynamics () -> Dynamics ()-enqueueWithPoints q xs (Dynamics m) = loop xs- where loop [] = return ()- loop (x : xs) = enqueue q (pointTime x) $ - Dynamics $ \p ->- do m x -- N.B. we substitute the time point!- let Dynamics m' = loop xs- m' p---- | Actuate the event handler in the integration time points.-enqueueWithIntegTimes :: EventQueue -> Dynamics () -> Dynamics ()-enqueueWithIntegTimes q m =- Dynamics $ \p ->- do let sc = pointSpecs p- (nl, nu) = integIterationBnds sc- points = map point [nl .. nu]- point n = Point { pointSpecs = sc,- pointRun = pointRun p,- pointTime = basicTime sc n 0,- pointIteration = n,- pointPhase = 0 }- Dynamics m' = enqueueWithPoints q points m- m' p---- | Actuate the event handler in the start time.-enqueueWithStartTime :: EventQueue -> Dynamics () -> Dynamics ()-enqueueWithStartTime q m =- Dynamics $ \p ->- do let sc = pointSpecs p- (nl, nu) = integIterationBnds sc- point n = Point { pointSpecs = sc,- pointRun = pointRun p,- pointTime = basicTime sc n 0,- pointIteration = n,- pointPhase = 0 }- Dynamics m' = enqueueWithPoints q [point nl] m- m' p---- | Actuate the event handler in the stop time.-enqueueWithStopTime :: EventQueue -> Dynamics () -> Dynamics ()-enqueueWithStopTime q m =- Dynamics $ \p ->- do let sc = pointSpecs p- (nl, nu) = integIterationBnds sc- point n = Point { pointSpecs = sc,- pointRun = pointRun p,- pointTime = basicTime sc n 0,- pointIteration = n,- pointPhase = 0 }- Dynamics m' = enqueueWithPoints q [point nu] m- m' p---- | Actuate the event handler in the current time but --- through the event queue, which allows continuing the --- current tasks and then calling the handler after the --- tasks are finished. The simulation time will be the same.-enqueueWithCurrentTime :: EventQueue -> Dynamics () -> Dynamics ()-enqueueWithCurrentTime q m =- Dynamics $ \p ->- do let Dynamics m' = enqueue q (pointTime p) m- m' p
− Simulation/Aivika/Dynamics/FIFO.hs
@@ -1,207 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.FIFO--- Copyright : Copyright (c) 2009-2013, 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 FIFO queue.----module Simulation.Aivika.Dynamics.FIFO- (FIFO,- fifoQueue,- fifoNull,- fifoFull,- fifoMaxCount,- fifoCount,- fifoLostCount,- fifoEnqueue,- fifoDequeue,- fifoEnqueueLost,- newFIFO,- dequeueFIFO,- tryDequeueFIFO,- enqueueFIFO,- tryEnqueueFIFO,- enqueueFIFOOrLost) where--import Data.IORef-import Data.Array-import Data.Array.IO.Safe--import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Process-import Simulation.Aivika.Dynamics.Resource-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Signal---- | Represents the FIFO queue with rule: first input - first output.-data FIFO a =- FIFO { fifoQueue :: EventQueue, -- ^ Return the event queue.- fifoMaxCount :: Int, -- ^ The maximum available number of items.- fifoReadRes :: Resource,- fifoWriteRes :: Resource,- fifoCountRef :: IORef Int,- fifoLostCountRef :: IORef Int,- fifoStartRef :: IORef Int,- fifoEndRef :: IORef Int,- fifoArray :: IOArray Int a, - fifoEnqueueSource :: SignalSource a,- fifoEnqueueLostSource :: SignalSource a,- fifoDequeueSource :: SignalSource a,- fifoUpdatedSource :: SignalSource a }- --- | Create a new FIFO queue with the specified maximum available number of items. -newFIFO :: EventQueue -> Int -> Simulation (FIFO a) -newFIFO q count =- do i <- liftIO $ newIORef 0- l <- liftIO $ newIORef 0- s <- liftIO $ newIORef 0- e <- liftIO $ newIORef 0- a <- liftIO $ newArray_ (0, count - 1)- r <- newResourceWithCount q count 0- w <- newResourceWithCount q count count- s1 <- newSignalSourceUnsafe- s2 <- newSignalSourceUnsafe- s3 <- newSignalSourceUnsafe- s4 <- newSignalSource q- return FIFO { fifoQueue = q,- fifoMaxCount = count,- fifoReadRes = r,- fifoWriteRes = w,- fifoCountRef = i,- fifoLostCountRef = l,- fifoStartRef = s,- fifoEndRef = e,- fifoArray = a, - fifoEnqueueSource = s1,- fifoEnqueueLostSource = s2,- fifoDequeueSource = s3,- fifoUpdatedSource = s4 }- --- | Test whether the FIFO queue is empty.-fifoNull :: FIFO a -> Dynamics Bool-fifoNull fifo =- do a <- fifoCount fifo- return (a == 0)---- | Test whether the FIFO queue is full.-fifoFull :: FIFO a -> Dynamics Bool-fifoFull fifo =- do a <- fifoCount fifo- return (a == fifoMaxCount fifo)---- | Return the queue size.-fifoCount :: FIFO a -> Dynamics Int-fifoCount fifo =- liftIO $ readIORef (fifoCountRef fifo)- --- | Return the number of lost items.-fifoLostCount :: FIFO a -> Dynamics Int-fifoLostCount fifo =- liftIO $ readIORef (fifoLostCountRef fifo)- --- | Dequeue from the FIFO queue suspending the process if--- the queue is empty.-dequeueFIFO :: FIFO a -> Process a -dequeueFIFO fifo =- do requestResource (fifoReadRes fifo)- a <- liftIO $ dequeueImpl fifo- releaseResource (fifoWriteRes fifo)- liftDynamics $ triggerSignal (fifoDequeueSource fifo) a- return a- --- | Try to dequeue from the FIFO queue immediately. -tryDequeueFIFO :: FIFO a -> Dynamics (Maybe a)-tryDequeueFIFO fifo =- do x <- tryRequestResourceInDynamics (fifoReadRes fifo)- if x - then do a <- liftIO $ dequeueImpl fifo- releaseResourceInDynamics (fifoWriteRes fifo)- triggerSignal (fifoDequeueSource fifo) a- return $ Just a- else return Nothing---- | Enqueue the item in the FIFO queue suspending the process--- if the queue is full. -enqueueFIFO :: FIFO a -> a -> Process ()-enqueueFIFO fifo a =- do requestResource (fifoWriteRes fifo)- liftIO $ enqueueImpl fifo a- releaseResource (fifoReadRes fifo)- liftDynamics $ triggerSignal (fifoEnqueueSource fifo) a- --- | Try to enqueue the item in the FIFO queue. Return 'False' in--- the monad if the queue is full.-tryEnqueueFIFO :: FIFO a -> a -> Dynamics Bool-tryEnqueueFIFO fifo a =- do x <- tryRequestResourceInDynamics (fifoWriteRes fifo)- if x - then do liftIO $ enqueueImpl fifo a- releaseResourceInDynamics (fifoReadRes fifo)- triggerSignal (fifoEnqueueSource fifo) a- return True- else return False---- | Try to enqueue the item in the FIFO queue. If the queue is full--- then the item will be lost.-enqueueFIFOOrLost :: FIFO a -> a -> Dynamics ()-enqueueFIFOOrLost fifo a =- do x <- tryRequestResourceInDynamics (fifoWriteRes fifo)- if x- then do liftIO $ enqueueImpl fifo a- releaseResourceInDynamics (fifoReadRes fifo)- triggerSignal (fifoEnqueueSource fifo) a- else do liftIO $ modifyIORef (fifoLostCountRef fifo) $ (+) 1- triggerSignal (fifoEnqueueLostSource fifo) a---- | Return a signal that notifies when any item is enqueued.-fifoEnqueue :: FIFO a -> Signal a-fifoEnqueue fifo = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (fifoUpdatedSource fifo)- m2 = publishSignal (fifoEnqueueSource fifo)---- | Return a signal which notifies that the item was lost when --- attempting to add it to the full queue with help of--- 'enqueueFIFOOrLost'.-fifoEnqueueLost :: FIFO a -> Signal a-fifoEnqueueLost fifo = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (fifoUpdatedSource fifo)- m2 = publishSignal (fifoEnqueueLostSource fifo)---- | Return a signal that notifies when any item is dequeued.-fifoDequeue :: FIFO a -> Signal a-fifoDequeue fifo = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (fifoUpdatedSource fifo)- m2 = publishSignal (fifoDequeueSource fifo)---- | An implementation method.-dequeueImpl :: FIFO a -> IO a-dequeueImpl fifo =- do i <- readIORef (fifoCountRef fifo)- s <- readIORef (fifoStartRef fifo)- let i' = i - 1- s' = (s + 1) `mod` fifoMaxCount fifo- a <- readArray (fifoArray fifo) s- writeArray (fifoArray fifo) s undefined- i' `seq` writeIORef (fifoCountRef fifo) i'- s' `seq` writeIORef (fifoStartRef fifo) s'- return a---- | An implementation method.-enqueueImpl :: FIFO a -> a -> IO ()-enqueueImpl fifo a =- do i <- readIORef (fifoCountRef fifo)- e <- readIORef (fifoEndRef fifo)- let i' = i + 1- e' = (e + 1) `mod` fifoMaxCount fifo- a `seq` writeArray (fifoArray fifo) e a- i' `seq` writeIORef (fifoCountRef fifo) i'- e' `seq` writeIORef (fifoEndRef fifo) e'
+ Simulation/Aivika/Dynamics/Fold.hs view
@@ -0,0 +1,82 @@++-- |+-- Module : Simulation.Aivika.Dynamics.Fold+-- Copyright : Copyright (c) 2009-2013, 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 fold functions that allows traversing the values of+-- any 'Dynamics' computation in the integration time points.+--+module Simulation.Aivika.Dynamics.Fold+ (foldDynamics1,+ foldDynamics) where++import Data.IORef+import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics+import Simulation.Aivika.Dynamics.Memo++--+-- Fold+--++-- | Like the standard 'foldl1' function but applied to values in +-- the integration time points. The accumulator values are transformed+-- according to the first argument, which should be either function +-- 'memo0Dynamics' or its unboxed version.+foldDynamics1 :: (Dynamics a -> Simulation (Dynamics a))+ -> (a -> a -> a) + -> Dynamics a + -> Simulation (Dynamics a)+foldDynamics1 tr f (Dynamics m) =+ do r <- liftIO $ newIORef m+ let z = Dynamics $ \p ->+ case pointIteration p of+ 0 -> + m p+ n -> do + let sc = pointSpecs p+ ty = basicTime sc (n - 1) 0+ py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }+ y <- readIORef r+ s <- y py+ x <- m p+ return $! f s x+ y@(Dynamics m) <- tr z+ liftIO $ writeIORef r m+ return y++-- | Like the standard 'foldl' function but applied to values in +-- the integration time points. The accumulator values are transformed+-- according to the first argument, which should be either function+-- 'memo0Dynamics' or its unboxed version.+foldDynamics :: (Dynamics a -> Simulation (Dynamics a))+ -> (a -> b -> a) + -> a+ -> Dynamics b + -> Simulation (Dynamics a)+foldDynamics tr f acc (Dynamics m) =+ do r <- liftIO $ newIORef $ const $ return acc+ let z = Dynamics $ \p ->+ case pointIteration p of+ 0 -> do+ x <- m p+ return $! f acc x+ n -> do + let sc = pointSpecs p+ ty = basicTime sc (n - 1) 0+ py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }+ y <- readIORef r+ s <- y py+ x <- m p+ return $! f s x+ y@(Dynamics m) <- tr z+ liftIO $ writeIORef r m+ return y
− Simulation/Aivika/Dynamics/Internal/Cont.hs
@@ -1,304 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Internal.Cont--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ The 'Cont' monad is a variation of the standard Cont monad --- and F# async workflow, where the result of applying --- the continuation is a dynamic process.----module Simulation.Aivika.Dynamics.Internal.Cont- (Cont(..),- ContParams,- runCont,- catchCont,- finallyCont,- throwCont,- resumeContByParams,- contParamsCanceled) where--import Data.IORef--import qualified Control.Exception as C-import Control.Exception (IOException, throw)--import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics---- | The 'Cont' type is similar to the standard Cont monad --- and F# async workflow but only the continuations return--- a dynamic process as a result.-newtype Cont a = Cont (ContParams a -> Dynamics ())---- | The continuation parameters.-data ContParams a = - ContParams { contCont :: a -> Dynamics (), - contAux :: ContParamsAux }---- | The auxiliary continuation parameters.-data ContParamsAux =- ContParamsAux { contECont :: IOException -> Dynamics (),- contCCont :: () -> Dynamics (),- contCancelRef :: IORef Bool, - contCatchFlag :: Bool }--instance Monad Cont where- return = returnC- m >>= k = bindC m k--instance SimulationLift Cont where- liftSimulation = liftSC--instance DynamicsLift Cont where- liftDynamics = liftDC--instance Functor Cont where- fmap = liftM--instance MonadIO Cont where- liftIO = liftIOC --invokeC :: Cont a -> ContParams a -> Dynamics ()-{-# INLINE invokeC #-}-invokeC (Cont m) = m--invokeD :: Point -> Dynamics a -> IO a-{-# INLINE invokeD #-}-invokeD p (Dynamics m) = m p--cancelD :: Point -> ContParams a -> IO ()-{-# NOINLINE cancelD #-}-cancelD p c =- do writeIORef (contCancelRef . contAux $ c) False- invokeD p $ (contCCont . contAux $ c) ()--returnC :: a -> Cont a-{-# INLINE returnC #-}-returnC a = - Cont $ \c ->- Dynamics $ \p ->- do z <- readIORef $ (contCancelRef . contAux) c- if z - then cancelD p c- else invokeD p $ contCont c a- --- bindC :: Cont a -> (a -> Cont b) -> Cont b--- {-# INLINE bindC #-}--- bindC m k = --- Cont $ \c -> --- if (contCatchFlag . contAux $ c) --- then bindWithCatch m k c--- else bindWithoutCatch m k c- -bindC :: Cont a -> (a -> Cont b) -> Cont b-{-# INLINE bindC #-}-bindC m k = - Cont $ bindWithoutCatch m k -- Another version is not tail recursive!- -bindWithoutCatch :: Cont a -> (a -> Cont b) -> ContParams b -> Dynamics ()-{-# INLINE bindWithoutCatch #-}-bindWithoutCatch (Cont m) k c = - Dynamics $ \p ->- do z <- readIORef $ (contCancelRef . contAux) c- if z - then cancelD p c- else invokeD p $ m $ - let cont a = invokeC (k a) c- in c { contCont = cont }---- It is not tail recursive!-bindWithCatch :: Cont a -> (a -> Cont b) -> ContParams b -> Dynamics ()-{-# NOINLINE bindWithCatch #-}-bindWithCatch (Cont m) k c = - Dynamics $ \p ->- do z <- readIORef $ (contCancelRef . contAux) c- if z - then cancelD p c- else invokeD p $ m $ - let cont a = catchDynamics - (invokeC (k a) c)- (contECont . contAux $ c)- in c { contCont = cont }---- Like "bindWithoutCatch (return a) k"-callWithoutCatch :: (a -> Cont b) -> a -> ContParams b -> Dynamics ()-callWithoutCatch k a c =- Dynamics $ \p ->- do z <- readIORef $ (contCancelRef . contAux) c- if z - then cancelD p c- else invokeD p $ invokeC (k a) c---- Like "bindWithCatch (return a) k" but it is not tail recursive!-callWithCatch :: (a -> Cont b) -> a -> ContParams b -> Dynamics ()-callWithCatch k a c =- Dynamics $ \p ->- do z <- readIORef $ (contCancelRef . contAux) c- if z - then cancelD p c- else invokeD p $ catchDynamics - (invokeC (k a) c)- (contECont . contAux $ c)---- | Exception handling within 'Cont' computations.-catchCont :: Cont a -> (IOException -> Cont a) -> Cont a-catchCont m h = - Cont $ \c -> - if contCatchFlag . contAux $ c- then catchWithCatch m h c- else error $- "To catch exceptions, the process must be created " ++- "with help of newProcessIDWithCatch: catchCont."- -catchWithCatch :: Cont a -> (IOException -> Cont a) -> ContParams a -> Dynamics ()-catchWithCatch (Cont m) h c =- Dynamics $ \p -> - do z <- readIORef $ (contCancelRef . contAux) c- if z - then cancelD p c- else invokeD p $ m $- -- let econt e = callWithCatch h e c -- not tail recursive!- let econt e = callWithoutCatch h e c- in c { contAux = (contAux c) { contECont = econt } }- --- | A computation with finalization part.-finallyCont :: Cont a -> Cont b -> Cont a-finallyCont m m' = - Cont $ \c -> - if contCatchFlag . contAux $ c- then finallyWithCatch m m' c- else error $- "To finalize computation, the process must be created " ++- "with help of newProcessIDWithCatch: finallyCont."- -finallyWithCatch :: Cont a -> Cont b -> ContParams a -> Dynamics () -finallyWithCatch (Cont m) (Cont m') c =- Dynamics $ \p ->- do z <- readIORef $ (contCancelRef . contAux) c- if z - then cancelD p c- else invokeD p $ m $- let cont a = - Dynamics $ \p ->- invokeD p $ m' $- let cont b = contCont c a- in c { contCont = cont }- econt e =- Dynamics $ \p ->- invokeD p $ m' $- let cont b = (contECont . contAux $ c) e- in c { contCont = cont }- ccont () = - Dynamics $ \p ->- invokeD p $ m' $- let cont b = (contCCont . contAux $ c) ()- econt e = (contCCont . contAux $ c) ()- in c { contCont = cont,- contAux = (contAux c) { contECont = econt } }- in c { contCont = cont,- contAux = (contAux c) { contECont = econt,- contCCont = ccont } }---- | Throw the exception with the further exception handling.--- By some reasons, the standard 'throw' function per se is not handled --- properly within 'Cont' computations, altough it will be still handled --- if it will be hidden under the 'liftIO' function. The problem arises --- namely with the @throw@ function, not 'IO' computations.-throwCont :: IOException -> Cont a-throwCont e = liftIO $ throw e---- | Run the 'Cont' computation with the specified cancelation token --- and flag indicating whether to catch exceptions.-runCont :: Cont a -> - (a -> Dynamics ()) ->- (IOError -> Dynamics ()) ->- (() -> Dynamics ()) ->- IORef Bool -> - Bool -> - Dynamics ()-runCont (Cont m) cont econt ccont cancelToken catchFlag = - m ContParams { contCont = cont,- contAux = - ContParamsAux { contECont = econt,- contCCont = ccont,- contCancelRef = cancelToken, - contCatchFlag = catchFlag } }---- | Lift the 'Simulation' computation.-liftSC :: Simulation a -> Cont a-liftSC (Simulation m) = - Cont $ \c ->- Dynamics $ \p ->- if contCatchFlag . contAux $ c- then liftIOWithCatch (m $ pointRun p) p c- else liftIOWithoutCatch (m $ pointRun p) p c- --- | Lift the 'Dynamics' computation.-liftDC :: Dynamics a -> Cont a-liftDC (Dynamics m) =- Cont $ \c ->- Dynamics $ \p ->- if contCatchFlag . contAux $ c- then liftIOWithCatch (m p) p c- else liftIOWithoutCatch (m p) p c- --- | Lift the IO computation.-liftIOC :: IO a -> Cont a-liftIOC m =- Cont $ \c ->- Dynamics $ \p ->- if contCatchFlag . contAux $ c- then liftIOWithCatch m p c- else liftIOWithoutCatch m p c- -liftIOWithoutCatch :: IO a -> Point -> ContParams a -> IO ()-{-# INLINE liftIOWithoutCatch #-}-liftIOWithoutCatch m p c =- do z <- readIORef $ (contCancelRef . contAux) c- if z- then cancelD p c- else do a <- m- invokeD p $ contCont c a--liftIOWithCatch :: IO a -> Point -> ContParams a -> IO ()-{-# NOINLINE liftIOWithCatch #-}-liftIOWithCatch m p c =- do z <- readIORef $ (contCancelRef . contAux) c- if z- then cancelD p c- else do aref <- newIORef undefined- eref <- newIORef Nothing- C.catch (m >>= writeIORef aref) - (writeIORef eref . Just)- e <- readIORef eref- case e of- Nothing -> - do a <- readIORef aref- -- tail recursive- invokeD p $ contCont c a- Just e ->- -- tail recursive- invokeD p $ (contECont . contAux) c e---- | Resume the computation by the specified parameters.-resumeContByParams :: ContParams a -> a -> Dynamics ()-{-# INLINE resumeContByParams #-}-resumeContByParams c a = - Dynamics $ \p ->- do z <- readIORef $ (contCancelRef . contAux) c- if z- then cancelD p c- else invokeD p $ contCont c a---- | Test whether the computation is canceled-contParamsCanceled :: ContParams a -> IO Bool-{-# INLINE contParamsCanceled #-}-contParamsCanceled c = - readIORef $ (contCancelRef . contAux) c
− Simulation/Aivika/Dynamics/Internal/Dynamics.hs
@@ -1,310 +0,0 @@--{-# LANGUAGE RecursiveDo #-}---- |--- Module : Simulation.Aivika.Dynamics.Internal.Dynamics--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ The module defines the 'Dynamics' monad representing an abstract dynamic --- process, i.e. a time varying polymorphic function. ----module Simulation.Aivika.Dynamics.Internal.Dynamics- (-- * Dynamics- Dynamics(..),- DynamicsLift(..),- Point(..),- runDynamicsInStartTime,- runDynamicsInStopTime,- runDynamicsInIntegTimes,- runDynamicsInTime,- runDynamicsInTimes,- -- * Error Handling- catchDynamics,- finallyDynamics,- throwDynamics,- -- * Utilities- basicTime,- integIterationBnds,- integIterationHiBnd,- integIterationLoBnd,- integPhaseBnds,- integPhaseHiBnd,- integPhaseLoBnd) where--import qualified Control.Exception as C-import Control.Exception (IOException, throw, finally)--import Control.Monad-import Control.Monad.Trans-import Control.Monad.Fix--import Simulation.Aivika.Dynamics.Internal.Simulation------- The Dynamics Monad------ A value of the Dynamics monad represents an abstract dynamic --- process, i.e. a time varying polymorphic function. This is --- a key point of the Aivika simulation library.------- | A value in the 'Dynamics' monad represents a dynamic process, i.e.--- a polymorphic time varying function.-newtype Dynamics a = Dynamics (Point -> IO a)---- | It defines the simulation point appended with the additional information.-data Point = Point { pointSpecs :: Specs, -- ^ the simulation specs- pointRun :: Run, -- ^ the simulation run- pointTime :: Double, -- ^ the current time- pointIteration :: Int, -- ^ the current iteration- pointPhase :: Int -- ^ the current phase- } deriving (Eq, Ord, Show)- --- | Returns the integration iterations starting from zero.-integIterations :: Specs -> [Int]-integIterations sc = [i1 .. i2] where- i1 = 0- i2 = round ((spcStopTime sc - - spcStartTime sc) / spcDT sc)---- | Returns the first and last integration iterations.-integIterationBnds :: Specs -> (Int, Int)-integIterationBnds sc = (0, round ((spcStopTime sc - - spcStartTime sc) / spcDT sc))---- | Returns the first integration iteration, i.e. zero.-integIterationLoBnd :: Specs -> Int-integIterationLoBnd sc = 0---- | Returns the last integration iteration.-integIterationHiBnd :: Specs -> Int-integIterationHiBnd sc = round ((spcStopTime sc - - spcStartTime sc) / spcDT sc)---- | Returns the phases for the specified simulation specs starting from zero.-integPhases :: Specs -> [Int]-integPhases sc = - case spcMethod sc of- Euler -> [0]- RungeKutta2 -> [0, 1]- RungeKutta4 -> [0, 1, 2, 3]---- | Returns the first and last integration phases.-integPhaseBnds :: Specs -> (Int, Int)-integPhaseBnds sc = - case spcMethod sc of- Euler -> (0, 0)- RungeKutta2 -> (0, 1)- RungeKutta4 -> (0, 3)---- | Returns the first integration phase, i.e. zero.-integPhaseLoBnd :: Specs -> Int-integPhaseLoBnd sc = 0- --- | Returns the last integration phase, 0 for Euler's method, 1 for RK2 and 3 for RK4.-integPhaseHiBnd :: Specs -> Int-integPhaseHiBnd sc = - case spcMethod sc of- Euler -> 0- RungeKutta2 -> 1- RungeKutta4 -> 3---- | Returns a simulation time for the integration point specified by --- the specs, iteration and phase.-basicTime :: Specs -> Int -> Int -> Double-basicTime sc n ph =- if ph < 0 then - error "Incorrect phase: basicTime"- else- spcStartTime sc + n' * spcDT sc + delta (spcMethod sc) ph - where n' = fromIntegral n- delta Euler 0 = 0- delta RungeKutta2 0 = 0- delta RungeKutta2 1 = spcDT sc- delta RungeKutta4 0 = 0- delta RungeKutta4 1 = spcDT sc / 2- delta RungeKutta4 2 = spcDT sc / 2- delta RungeKutta4 3 = spcDT sc--instance Monad Dynamics where- return = returnD- m >>= k = bindD m k--returnD :: a -> Dynamics a-returnD a = Dynamics (\p -> return a)--bindD :: Dynamics a -> (a -> Dynamics b) -> Dynamics b-bindD (Dynamics m) k = - Dynamics $ \p -> - do a <- m p- let Dynamics m' = k a- m' p---- | Run the dynamic process in the initial simulation point.-runDynamicsInStartTime :: Dynamics a -> Simulation a-runDynamicsInStartTime (Dynamics m) =- Simulation $ \r ->- do let sc = runSpecs r - n = 0- t = spcStartTime sc- m Point { pointSpecs = sc,- pointRun = r,- pointTime = t,- pointIteration = n,- pointPhase = 0 }---- | Run the dynamic process in the final simulation point.-runDynamicsInStopTime :: Dynamics a -> Simulation a-runDynamicsInStopTime (Dynamics m) =- Simulation $ \r ->- do let sc = runSpecs r - n = integIterationHiBnd sc- t = basicTime sc n 0- m Point { pointSpecs = sc,- pointRun = r,- pointTime = t,- pointIteration = n,- pointPhase = 0 }---- | Run the dynamic process in all integration time points-runDynamicsInIntegTimes :: Dynamics a -> Simulation [IO a]-runDynamicsInIntegTimes (Dynamics m) =- Simulation $ \r ->- do let sc = runSpecs r- (nl, nu) = integIterationBnds sc- point n = Point { pointSpecs = sc,- pointRun = r,- pointTime = basicTime sc n 0,- pointIteration = n,- pointPhase = 0 }- return $ map (m . point) [nl .. nu]---- | Run the dynamic process in the specified time point.-runDynamicsInTime :: Double -> Dynamics a -> Simulation a-runDynamicsInTime t (Dynamics m) =- Simulation $ \r ->- do let sc = runSpecs r- t0 = spcStartTime sc- dt = spcDT sc- n = fromIntegral $ floor ((t - t0) / dt)- m Point { pointSpecs = sc,- pointRun = r,- pointTime = t,- pointIteration = n,- pointPhase = -1 }---- | Run the dynamic process in the specified time points.-runDynamicsInTimes :: [Double] -> Dynamics a -> Simulation [IO a]-runDynamicsInTimes ts (Dynamics m) =- Simulation $ \r ->- do let sc = runSpecs r- t0 = spcStartTime sc- dt = spcDT sc- point t =- let n = fromIntegral $ floor ((t - t0) / dt)- in Point { pointSpecs = sc,- pointRun = r,- pointTime = t,- pointIteration = n,- pointPhase = -1 }- return $ map (m . point) ts--instance Functor Dynamics where- fmap = liftMD--instance Eq (Dynamics a) where- x == y = error "Can't compare dynamics." --instance Show (Dynamics a) where- showsPrec _ x = showString "<< Dynamics >>"--liftMD :: (a -> b) -> Dynamics a -> Dynamics b-{-# INLINE liftMD #-}-liftMD f (Dynamics x) =- Dynamics $ \p -> do { a <- x p; return $ f a }--liftM2D :: (a -> b -> c) -> Dynamics a -> Dynamics b -> Dynamics c-{-# INLINE liftM2D #-}-liftM2D f (Dynamics x) (Dynamics y) =- Dynamics $ \p -> do { a <- x p; b <- y p; return $ f a b }--instance (Num a) => Num (Dynamics a) where- x + y = liftM2D (+) x y- x - y = liftM2D (-) x y- x * y = liftM2D (*) x y- negate = liftMD negate- abs = liftMD abs- signum = liftMD signum- fromInteger i = return $ fromInteger i--instance (Fractional a) => Fractional (Dynamics a) where- x / y = liftM2D (/) x y- recip = liftMD recip- fromRational t = return $ fromRational t--instance (Floating a) => Floating (Dynamics a) where- pi = return pi- exp = liftMD exp- log = liftMD log- sqrt = liftMD sqrt- x ** y = liftM2D (**) x y- sin = liftMD sin- cos = liftMD cos- tan = liftMD tan- asin = liftMD asin- acos = liftMD acos- atan = liftMD atan- sinh = liftMD sinh- cosh = liftMD cosh- tanh = liftMD tanh- asinh = liftMD asinh- acosh = liftMD acosh- atanh = liftMD atanh--instance MonadIO Dynamics where- liftIO m = Dynamics $ const m--instance SimulationLift Dynamics where- liftSimulation = liftDS- -liftDS :: Simulation a -> Dynamics a-{-# INLINE liftDS #-}-liftDS (Simulation m) =- Dynamics $ \p -> m $ pointRun p---- | A type class to lift the 'Dynamics' computations in other monads.-class Monad m => DynamicsLift m where- - -- | Lift the specified 'Dynamics' computation in another monad.- liftDynamics :: Dynamics a -> m a- --- | Exception handling within 'Dynamics' computations.-catchDynamics :: Dynamics a -> (IOException -> Dynamics a) -> Dynamics a-catchDynamics (Dynamics m) h =- Dynamics $ \p -> - C.catch (m p) $ \e ->- let Dynamics m' = h e in m' p- --- | A computation with finalization part like the 'finally' function.-finallyDynamics :: Dynamics a -> Dynamics b -> Dynamics a-finallyDynamics (Dynamics m) (Dynamics m') =- Dynamics $ \p ->- C.finally (m p) (m' p)---- | Like the standard 'throw' function.-throwDynamics :: IOException -> Dynamics a-throwDynamics = throw---- | Invoke the 'Dynamics' computation.-invokeDynamics :: Dynamics a -> Point -> IO a-{-# INLINE invokeDynamics #-}-invokeDynamics (Dynamics m) p = m p--instance MonadFix Dynamics where- mfix f = - Dynamics $ \p ->- do { rec { a <- invokeDynamics (f a) p }; return a }
− Simulation/Aivika/Dynamics/Internal/Fold.hs
@@ -1,92 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Internal.Fold--- Copyright : Copyright (c) 2009-2013, 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 fold functions that allows traversing the values of--- any dynamic process in the integration time points.----module Simulation.Aivika.Dynamics.Internal.Fold- (foldDynamics1,- foldDynamics,- divideDynamics) where--import Data.IORef-import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Interpolate-import Simulation.Aivika.Dynamics.Internal.Memo------- Fold------- | Like the standard 'foldl1' function but applied to values in --- the integration time points. The accumulator values are transformed--- according to the first argument, which should be either function --- 'memo0' or 'umemo0'.-foldDynamics1 :: (Dynamics a -> Simulation (Dynamics a))- -> (a -> a -> a) - -> Dynamics a - -> Simulation (Dynamics a)-foldDynamics1 tr f (Dynamics m) =- do r <- liftIO $ newIORef m- let z = Dynamics $ \p ->- case pointIteration p of- 0 -> - m p- n -> do - let sc = pointSpecs p- ty = basicTime sc (n - 1) 0- py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }- y <- readIORef r- s <- y py- x <- m p- return $! f s x- y@(Dynamics m) <- tr z- liftIO $ writeIORef r m- return y---- | Like the standard 'foldl' function but applied to values in --- the integration time points. The accumulator values are transformed--- according to the first argument, which should be either function--- 'memo0' or 'umemo0'.-foldDynamics :: (Dynamics a -> Simulation (Dynamics a))- -> (a -> b -> a) - -> a- -> Dynamics b - -> Simulation (Dynamics a)-foldDynamics tr f acc (Dynamics m) =- do r <- liftIO $ newIORef $ const $ return acc- let z = Dynamics $ \p ->- case pointIteration p of- 0 -> do- x <- m p- return $! f acc x- n -> do - let sc = pointSpecs p- ty = basicTime sc (n - 1) 0- py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }- y <- readIORef r- s <- y py- x <- m p- return $! f s x- y@(Dynamics m) <- tr z- liftIO $ writeIORef r m- return y---- | Divide the values in integration time points by the number of--- the current iteration. It can be useful for statistic functions in--- combination with the fold.-divideDynamics :: Dynamics Double -> Dynamics Double-divideDynamics (Dynamics m) = - discrete $ Dynamics $ \p ->- do a <- m p- return $ a / fromIntegral (pointIteration p + 1)
− Simulation/Aivika/Dynamics/Internal/Interpolate.hs
@@ -1,62 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Internal.Interpolate--- Copyright : Copyright (c) 2009-2013, 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 interpolation functions.--- These functions complement the memoization, possibly except for --- the 'initD' function which is useful to get an initial --- value of any dynamic process.-----module Simulation.Aivika.Dynamics.Internal.Interpolate- (initDynamics,- discrete,- interpolate) where--import Simulation.Aivika.Dynamics.Internal.Dynamics---- | Return the initial value.-initDynamics :: Dynamics a -> Dynamics a-{-# INLINE initDynamics #-}-initDynamics (Dynamics m) =- Dynamics $ \p ->- if pointIteration p == 0 && pointPhase p == 0 then- m p- else- let sc = pointSpecs p- in m $ p { pointTime = basicTime sc 0 0,- pointIteration = 0,- pointPhase = 0 } ---- | Discretize the computation in the integration time points.-discrete :: Dynamics a -> Dynamics a-{-# INLINE discrete #-}-discrete (Dynamics m) =- Dynamics $ \p ->- if pointPhase p == 0 then- m p- else- let sc = pointSpecs p- n = pointIteration p- in m $ p { pointTime = basicTime sc n 0,- pointPhase = 0 }---- | Interpolate the computation based on the integration time points only.--- Unlike the 'discrete' function it knows about the intermediate time points --- that are used in the Runge-Kutta method.-interpolate :: Dynamics a -> Dynamics a-{-# INLINE interpolate #-}-interpolate (Dynamics m) = - Dynamics $ \p -> - if pointPhase p >= 0 then - m p- else - let sc = pointSpecs p- n = pointIteration p- in m $ p { pointTime = basicTime sc n 0,- pointPhase = 0 }
− Simulation/Aivika/Dynamics/Internal/Memo.hs
@@ -1,196 +0,0 @@--{-# LANGUAGE FlexibleContexts #-}---- |--- Module : Simulation.Aivika.Dynamics.Internal.Memo--- Copyright : Copyright (c) 2009-2013, 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 memo functions. The memoization creates such dynamic processes, --- which values are cached in the integration time points. Then these values are --- interpolated in all other time points.-----module Simulation.Aivika.Dynamics.Internal.Memo- (memo,- umemo,- memo0,- umemo0,- iterateDynamics) where--import Data.Array-import Data.Array.IO.Safe-import Data.IORef-import Control.Monad--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Interpolate--newMemoArray_ :: Ix i => (i, i) -> IO (IOArray i e)-newMemoArray_ = newArray_--newMemoUArray_ :: (MArray IOUArray e IO, Ix i) => (i, i) -> IO (IOUArray i e)-newMemoUArray_ = newArray_---- | Memoize and order the computation in the integration time points using --- the interpolation that knows of the Runge-Kutta method.-memo :: Dynamics e -> Simulation (Dynamics e)-{-# INLINE memo #-}-memo (Dynamics m) = - Simulation $ \r ->- do let sc = runSpecs r- (phl, phu) = integPhaseBnds sc- (nl, nu) = integIterationBnds sc- arr <- newMemoArray_ ((phl, nl), (phu, nu))- nref <- newIORef 0- phref <- newIORef 0- let r p = - do let sc = pointSpecs p- n = pointIteration p- ph = pointPhase p- phu = integPhaseHiBnd sc - loop n' ph' = - if (n' > n) || ((n' == n) && (ph' > ph)) - then - readArray arr (ph, n)- else - let p' = p { pointIteration = n', pointPhase = ph',- pointTime = basicTime sc n' ph' }- in do a <- m p'- a `seq` writeArray arr (ph', n') a- if ph' >= phu - then do writeIORef phref 0- writeIORef nref (n' + 1)- loop (n' + 1) 0- else do writeIORef phref (ph' + 1)- loop n' (ph' + 1)- n' <- readIORef nref- ph' <- readIORef phref- loop n' ph'- return $ interpolate $ Dynamics r---- | This is a more efficient version the 'memo' function which uses --- an unboxed array to store the values.-umemo :: (MArray IOUArray e IO) => Dynamics e -> Simulation (Dynamics e)-{-# INLINE umemo #-}-umemo (Dynamics m) = - Simulation $ \r ->- do let sc = runSpecs r- (phl, phu) = integPhaseBnds sc- (nl, nu) = integIterationBnds sc- arr <- newMemoUArray_ ((phl, nl), (phu, nu))- nref <- newIORef 0- phref <- newIORef 0- let r p =- do let sc = pointSpecs p- n = pointIteration p- ph = pointPhase p- phu = integPhaseHiBnd sc - loop n' ph' = - if (n' > n) || ((n' == n) && (ph' > ph)) - then - readArray arr (ph, n)- else - let p' = p { pointIteration = n', - pointPhase = ph',- pointTime = basicTime sc n' ph' }- in do a <- m p'- a `seq` writeArray arr (ph', n') a- if ph' >= phu - then do writeIORef phref 0- writeIORef nref (n' + 1)- loop (n' + 1) 0- else do writeIORef phref (ph' + 1)- loop n' (ph' + 1)- n' <- readIORef nref- ph' <- readIORef phref- loop n' ph'- return $ interpolate $ Dynamics r---- | Memoize and order the computation in the integration time points using --- the 'discrete' interpolation. It consumes less memory than the 'memo'--- function but it is not aware of the Runge-Kutta method. There is a subtle--- difference when we request for values in the intermediate time points--- that are used by this method to integrate. In general case you should --- prefer the 'memo0' function above 'memo'.-memo0 :: Dynamics e -> Simulation (Dynamics e)-{-# INLINE memo0 #-}-memo0 (Dynamics m) = - Simulation $ \r ->- do let sc = runSpecs r- bnds = integIterationBnds sc- arr <- newMemoArray_ bnds- nref <- newIORef 0- let r p =- do let sc = pointSpecs p- n = pointIteration p- loop n' = - if n' > n- then - readArray arr n- else - let p' = p { pointIteration = n', pointPhase = 0,- pointTime = basicTime sc n' 0 }- in do a <- m p'- a `seq` writeArray arr n' a- writeIORef nref (n' + 1)- loop (n' + 1)- n' <- readIORef nref- loop n'- return $ discrete $ Dynamics r---- | This is a more efficient version the 'memo0' function which uses --- an unboxed array to store the values.-umemo0 :: (MArray IOUArray e IO) => Dynamics e -> Simulation (Dynamics e)-{-# INLINE umemo0 #-}-umemo0 (Dynamics m) = - Simulation $ \r ->- do let sc = runSpecs r- bnds = integIterationBnds sc- arr <- newMemoUArray_ bnds- nref <- newIORef 0- let r p =- do let sc = pointSpecs p- n = pointIteration p- loop n' = - if n' > n- then - readArray arr n- else - let p' = p { pointIteration = n', pointPhase = 0,- pointTime = basicTime sc n' 0 }- in do a <- m p'- a `seq` writeArray arr n' a- writeIORef nref (n' + 1)- loop (n' + 1)- n' <- readIORef nref- loop n'- return $ discrete $ Dynamics r---- | Iterate sequentially the dynamic process with side effects in --- the integration time points. It is equivalent to a call of the--- 'memo0' function but significantly more efficient, for the array --- is not created.-iterateDynamics :: Dynamics () -> Simulation (Dynamics ())-{-# INLINE iterateDynamics #-}-iterateDynamics (Dynamics m) = - Simulation $ \r ->- do let sc = runSpecs r- nref <- newIORef 0- let r p =- do let sc = pointSpecs p- n = pointIteration p- loop n' = - unless (n' > n) $- let p' = p { pointIteration = n', pointPhase = 0,- pointTime = basicTime sc n' 0 }- in do a <- m p'- a `seq` writeIORef nref (n' + 1)- loop (n' + 1)- n' <- readIORef nref- loop n'- return $ discrete $ Dynamics r
− Simulation/Aivika/Dynamics/Internal/Process.hs
@@ -1,300 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Internal.Process--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ A value in the 'Process' monad represents a discontinuous process that --- can suspend in any simulation time point and then resume later in the same --- or another time point. --- --- The process of this type behaves like a dynamic process too. So, any value --- in the 'Dynamics' monad can be lifted to the Process monad. Moreover, --- a value in the Process monad can be run in the Dynamics monad.------ A value of the 'ProcessID' type is just an identifier of such a process.----module Simulation.Aivika.Dynamics.Internal.Process- (ProcessID,- Process(..),- processQueue,- newProcessID,- newProcessIDWithCatch,- holdProcess,- interruptProcess,- processInterrupted,- passivateProcess,- processPassive,- reactivateProcess,- processID,- cancelProcess,- processCanceled,- runProcess,- runProcessNow,- catchProcess,- finallyProcess,- throwProcess) where--import Data.Maybe-import Data.IORef-import Control.Exception (IOException, throw)-import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Cont-import Simulation.Aivika.Dynamics.EventQueue---- | Represents a process identificator.-data ProcessID = - ProcessID { processQueue :: EventQueue, -- ^ Return the event queue.- processStarted :: IORef Bool,- processCatchFlag :: Bool,- processReactCont :: IORef (Maybe (ContParams ())), - processCancelRef :: IORef Bool, - processCancelToken :: IORef Bool,- processInterruptRef :: IORef Bool, - processInterruptCont :: IORef (Maybe (ContParams ())), - processInterruptVersion :: IORef Int }---- | Specifies a discontinuous process that can suspend at any time--- and then resume later.-newtype Process a = Process (ProcessID -> Cont a)---- | Hold the process for the specified time period.-holdProcess :: Double -> Process ()-holdProcess dt =- Process $ \pid ->- Cont $ \c ->- Dynamics $ \p ->- do let x = processInterruptCont pid- writeIORef x $ Just c- writeIORef (processInterruptRef pid) False- v <- readIORef (processInterruptVersion pid)- let Dynamics m = - enqueue (processQueue pid) (pointTime p + dt) $- Dynamics $ \p ->- do v' <- readIORef (processInterruptVersion pid)- when (v == v') $ - do writeIORef x Nothing- let Dynamics m = resumeContByParams c ()- m p- m p---- | Interrupt a process with the specified ID if the process--- was held by computation 'holdProcess'.-interruptProcess :: ProcessID -> Dynamics ()-interruptProcess pid =- Dynamics $ \p ->- do let x = processInterruptCont pid- a <- readIORef x- case a of- Nothing -> return ()- Just c ->- do writeIORef x Nothing- writeIORef (processInterruptRef pid) True- modifyIORef (processInterruptVersion pid) $ (+) 1- let Dynamics m = - enqueue (processQueue pid) (pointTime p) $ - resumeContByParams c ()- m p- --- | Test whether the process with the specified ID was interrupted.-processInterrupted :: ProcessID -> Dynamics Bool-processInterrupted pid =- Dynamics $ \p ->- readIORef (processInterruptRef pid)---- | Passivate the process.-passivateProcess :: Process ()-passivateProcess =- Process $ \pid ->- Cont $ \c ->- Dynamics $ \p ->- do let x = processReactCont pid- a <- readIORef x- case a of- Nothing -> writeIORef x $ Just c- Just _ -> error "Cannot passivate the process twice: passivate"---- | Test whether the process with the specified ID is passivated.-processPassive :: ProcessID -> Dynamics Bool-processPassive pid =- Dynamics $ \p ->- do let Dynamics m = runQueueSync $ processQueue pid- m p- let x = processReactCont pid- a <- readIORef x- return $ isJust a---- | Reactivate a process with the specified ID.-reactivateProcess :: ProcessID -> Dynamics ()-reactivateProcess pid =- Dynamics $ \p ->- do let Dynamics m = runQueueSync $ processQueue pid- m p- let x = processReactCont pid- a <- readIORef x- case a of- Nothing -> - return ()- Just c ->- do writeIORef x Nothing- let Dynamics m = enqueue (processQueue pid) (pointTime p) $ - resumeContByParams c ()- m p---- | Start the process with the specified ID at the desired time.-runProcess :: Process () -> ProcessID -> Double -> Dynamics ()-runProcess (Process p) pid t =- runCont m cont econt ccont (processCancelToken pid) (processCatchFlag pid)- where cont = return- econt = throw- ccont = return- m = do y <- liftIO $ readIORef (processStarted pid)- if y - then error $- "A process with such ID " ++- "has been started already: runProc"- else liftIO $ writeIORef (processStarted pid) True- Cont $ \c -> enqueue (processQueue pid) t $ - resumeContByParams c ()- p pid---- | Start the process with the specified ID at the current simulation time.-runProcessNow :: Process () -> ProcessID -> Dynamics ()-runProcessNow process pid =- Dynamics $ \p ->- do let Dynamics m = runProcess process pid (pointTime p)- m p---- | Return the current process ID.-processID :: Process ProcessID-processID = Process $ \pid -> return pid---- | Create a new process ID without exception handling.-newProcessID :: EventQueue -> Simulation ProcessID-newProcessID q =- do x <- liftIO $ newIORef Nothing- y <- liftIO $ newIORef False- c <- liftIO $ newIORef False- t <- liftIO $ newIORef False- i <- liftIO $ newIORef False- z <- liftIO $ newIORef Nothing- v <- liftIO $ newIORef 0- return ProcessID { processQueue = q,- processStarted = y,- processCatchFlag = False,- processReactCont = x, - processCancelRef = c, - processCancelToken = t,- processInterruptRef = i,- processInterruptCont = z, - processInterruptVersion = v }---- | Create a new process ID with capabilities of catching --- the IOError exceptions and finalizing the computation. --- The corresponded process will be slower than that one--- which identifier is created with help of 'newProcessID'.-newProcessIDWithCatch :: EventQueue -> Simulation ProcessID-newProcessIDWithCatch q =- do x <- liftIO $ newIORef Nothing- y <- liftIO $ newIORef False- c <- liftIO $ newIORef False- t <- liftIO $ newIORef False- i <- liftIO $ newIORef False- z <- liftIO $ newIORef Nothing- v <- liftIO $ newIORef 0- return ProcessID { processQueue = q,- processStarted = y,- processCatchFlag = True,- processReactCont = x, - processCancelRef = c, - processCancelToken = t,- processInterruptRef = i,- processInterruptCont = z, - processInterruptVersion = v }---- | Cancel a process with the specified ID.-cancelProcess :: ProcessID -> Dynamics ()-cancelProcess pid =- Dynamics $ \p ->- do z <- readIORef (processCancelRef pid) - unless z $- do writeIORef (processCancelRef pid) True- writeIORef (processCancelToken pid) True---- | Test whether the process with the specified ID is canceled.-processCanceled :: ProcessID -> Dynamics Bool-processCanceled pid =- Dynamics $ \p ->- readIORef (processCancelRef pid)--instance Eq ProcessID where- x == y = processReactCont x == processReactCont y -- for the references are unique--instance Monad Process where- return = returnP- m >>= k = bindP m k--instance Functor Process where- fmap = liftM--instance SimulationLift Process where- liftSimulation = liftSP- -instance DynamicsLift Process where- liftDynamics = liftDP- -instance MonadIO Process where- liftIO = liftIOP- -returnP :: a -> Process a-{-# INLINE returnP #-}-returnP a = Process $ \pid -> return a--bindP :: Process a -> (a -> Process b) -> Process b-{-# INLINE bindP #-}-bindP (Process m) k = - Process $ \pid -> - do a <- m pid- let Process m' = k a- m' pid--liftSP :: Simulation a -> Process a-{-# INLINE liftSP #-}-liftSP m = Process $ \pid -> liftSimulation m--liftDP :: Dynamics a -> Process a-{-# INLINE liftDP #-}-liftDP m = Process $ \pid -> liftDynamics m--liftIOP :: IO a -> Process a-{-# INLINE liftIOP #-}-liftIOP m = Process $ \pid -> liftIO m---- | Exception handling within 'Process' computations.-catchProcess :: Process a -> (IOException -> Process a) -> Process a-catchProcess (Process m) h =- Process $ \pid ->- catchCont (m pid) $ \e ->- let Process m' = h e in m' pid- --- | A computation with finalization part.-finallyProcess :: Process a -> Process b -> Process a-finallyProcess (Process m) (Process m') =- Process $ \pid ->- finallyCont (m pid) (m' pid)---- | Throw the exception with the further exception handling.--- By some reasons, the standard 'throw' function per se is not handled --- properly within 'Process' computations, although it will be still --- handled if it will be hidden under the 'liftIO' function. The problem --- arises namely with the @throw@ function, not 'IO' computations.-throwProcess :: IOException -> Process a-throwProcess = liftIO . throw-
− Simulation/Aivika/Dynamics/Internal/Signal.hs
@@ -1,347 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Internal.Signal--- Copyright : Copyright (c) 2009-2013, 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 signal which we can subscribe handlers to. --- These handlers can be disposed. The signal is triggered in the --- current time point actuating the corresponded computations from --- the handlers. -----module Simulation.Aivika.Dynamics.Internal.Signal- (Signal,- SignalSource,- newSignalSourceWithUpdate,- newSignalSourceUnsafe,- publishSignal,- triggerSignal,- handleSignal,- handleSignal_,- updateSignal,- mapSignal,- mapSignalM,- apSignal,- filterSignal,- filterSignalM,- emptySignal,- merge2Signals,- merge3Signals,- merge4Signals,- merge5Signals) where--import Data.IORef-import Data.Monoid--import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Simulation---- | The signal source that can publish its signal.-data SignalSource a =- SignalSource { publishSignal :: Signal a,- -- ^ Publish the signal.- triggerSignal :: a -> Dynamics ()- -- ^ Trigger the signal actuating - -- all its handlers at the current - -- simulation time point.- }- --- | The signal that can have disposable handlers. -data Signal a =- Signal { handleSignal :: (a -> Dynamics ()) -> - Dynamics (Dynamics ()),- -- ^ Subscribe the handler to the specified - -- signal and return a nested computation - -- that, being applied, unsubscribes the - -- handler from this signal.- --- -- If the signal is bound up with the event queue- -- then the signal in the current time is not lost- -- by the handler any more. Changed in version 0.6.1.- updateSignal :: Dynamics ()- -- ^ Update the signal to its actual state.- --- -- You will rarely need to call this function directly- -- as it is usually called implicitly.- --- -- Since version 0.6.1 it processes only those events- -- which time is less than the current simulation time- -- if the signal is bound up with the event queue,- -- although you can define your own 'updateSignal'- -- function when creating a new signal source with help of- -- 'newSignalSourceWithUpdate'.- }- --- | The queue of signal handlers.-data SignalHandlerQueue a =- SignalHandlerQueue { queueStart :: IORef (Maybe (SignalHandler a)),- queueEnd :: IORef (Maybe (SignalHandler a)) }- --- | It contains the information about the disposable queue handler.-data SignalHandler a =- SignalHandler { handlerComp :: a -> Dynamics (),- handlerPrev :: IORef (Maybe (SignalHandler a)),- handlerNext :: IORef (Maybe (SignalHandler a)) }---- | Subscribe the handler to the specified signal.--- To subscribe the disposable handlers, use function 'handleSignal'.------ If the signal is bound up with the event queue then the signal in--- the current time is not lost by the handler any more.--- Changed in version 0.6.1.-handleSignal_ :: Signal a -> (a -> Dynamics ()) -> Dynamics ()-handleSignal_ signal h = - do x <- handleSignal signal h- return ()- --- | Create a new signal source with the specified update computation.-newSignalSourceWithUpdate :: Dynamics () -> Simulation (SignalSource a)-newSignalSourceWithUpdate update =- Simulation $ \r ->- do start <- newIORef Nothing- end <- newIORef Nothing- let queue = SignalHandlerQueue { queueStart = start,- queueEnd = end }- signal = Signal { handleSignal = handle, - updateSignal = update }- source = SignalSource { publishSignal = signal, - triggerSignal = trigger }- handle h =- Dynamics $ \p ->- do invokeDynamics p update- x <- enqueueSignalHandler queue h- return $ - Dynamics $ \p ->- do invokeDynamics p update- dequeueSignalHandler queue x- trigger a =- Dynamics $ \p ->- do invokeDynamics p update - let h = queueStart queue- triggerSignalHandlers h a p- return source- --- | Create a new signal source that has no update computation.-newSignalSourceUnsafe :: Simulation (SignalSource a)-newSignalSourceUnsafe =- Simulation $ \r ->- do start <- newIORef Nothing- end <- newIORef Nothing- let queue = SignalHandlerQueue { queueStart = start,- queueEnd = end }- signal = Signal { handleSignal = handle, - updateSignal = update }- source = SignalSource { publishSignal = signal, - triggerSignal = trigger }- handle h =- Dynamics $ \p ->- do x <- enqueueSignalHandler queue h- return $ liftIO $ dequeueSignalHandler queue x- trigger a =- Dynamics $ \p ->- let h = queueStart queue- in triggerSignalHandlers h a p- update = return ()- return source---- | Trigger all next signal handlers.-triggerSignalHandlers :: IORef (Maybe (SignalHandler a)) -> a -> Point -> IO ()-{-# INLINE triggerSignalHandlers #-}-triggerSignalHandlers r a p =- do x <- readIORef r- case x of- Nothing -> return ()- Just h ->- do let Dynamics m = handlerComp h a- m p- triggerSignalHandlers (handlerNext h) a p- --- | Enqueue the handler and return its representative in the queue. -enqueueSignalHandler :: SignalHandlerQueue a -> (a -> Dynamics ()) -> IO (SignalHandler a)-enqueueSignalHandler q h = - do tail <- readIORef (queueEnd q)- case tail of- Nothing ->- do prev <- newIORef Nothing- next <- newIORef Nothing- let handler = SignalHandler { handlerComp = h,- handlerPrev = prev,- handlerNext = next }- writeIORef (queueStart q) (Just handler)- writeIORef (queueEnd q) (Just handler)- return handler- Just x ->- do prev <- newIORef tail- next <- newIORef Nothing- let handler = SignalHandler { handlerComp = h,- handlerPrev = prev,- handlerNext = next }- writeIORef (handlerNext x) (Just handler)- writeIORef (queueEnd q) (Just handler)- return handler---- | Dequeue the handler representative.-dequeueSignalHandler :: SignalHandlerQueue a -> SignalHandler a -> IO ()-dequeueSignalHandler q h = - do prev <- readIORef (handlerPrev h)- case prev of- Nothing ->- do next <- readIORef (handlerNext h)- case next of- Nothing ->- do writeIORef (queueStart q) Nothing- writeIORef (queueEnd q) Nothing- Just y ->- do writeIORef (handlerPrev y) Nothing- writeIORef (handlerNext h) Nothing- writeIORef (queueStart q) next- Just x ->- do next <- readIORef (handlerNext h)- case next of- Nothing ->- do writeIORef (handlerPrev h) Nothing- writeIORef (handlerNext x) Nothing- writeIORef (queueEnd q) prev- Just y ->- do writeIORef (handlerPrev h) Nothing- writeIORef (handlerNext h) Nothing- writeIORef (handlerPrev y) prev- writeIORef (handlerNext x) next--instance Functor Signal where- fmap = mapSignal- -instance Monoid (Signal a) where - - mempty = emptySignal- - mappend = merge2Signals- - mconcat [] = emptySignal- mconcat [x1] = x1- mconcat [x1, x2] = merge2Signals x1 x2- mconcat [x1, x2, x3] = merge3Signals x1 x2 x3- mconcat [x1, x2, x3, x4] = merge4Signals x1 x2 x3 x4- mconcat [x1, x2, x3, x4, x5] = merge5Signals x1 x2 x3 x4 x5- mconcat (x1 : x2 : x3 : x4 : x5 : xs) = - mconcat $ merge5Signals x1 x2 x3 x4 x5 : xs- --- | Map the signal according the specified function.-mapSignal :: (a -> b) -> Signal a -> Signal b-mapSignal f m =- Signal { handleSignal = \h -> - handleSignal m $ h . f, - updateSignal = - updateSignal m }---- | Filter only those signal values that satisfy to --- the specified predicate.-filterSignal :: (a -> Bool) -> Signal a -> Signal a-filterSignal p m =- Signal { handleSignal = \h ->- handleSignal m $ \a ->- when (p a) $ h a, - updateSignal =- updateSignal m }- --- | Filter only those signal values that satisfy to --- the specified predicate.-filterSignalM :: (a -> Dynamics Bool) -> Signal a -> Signal a-filterSignalM p m =- Signal { handleSignal = \h ->- handleSignal m $ \a ->- do x <- p a- when x $ h a, - updateSignal =- updateSignal m }- --- | Merge two signals.-merge2Signals :: Signal a -> Signal a -> Signal a-merge2Signals m1 m2 =- Signal { handleSignal = \h ->- do x1 <- handleSignal m1 h- x2 <- handleSignal m2 h- return $ do { x1; x2 }, - updateSignal =- do updateSignal m1- updateSignal m2 }---- | Merge three signals.-merge3Signals :: Signal a -> Signal a -> Signal a -> Signal a-merge3Signals m1 m2 m3 =- Signal { handleSignal = \h ->- do x1 <- handleSignal m1 h- x2 <- handleSignal m2 h- x3 <- handleSignal m3 h- return $ do { x1; x2; x3 },- updateSignal =- do updateSignal m1- updateSignal m2 - updateSignal m3 }---- | Merge four signals.-merge4Signals :: Signal a -> Signal a -> Signal a -> - Signal a -> Signal a-merge4Signals m1 m2 m3 m4 =- Signal { handleSignal = \h ->- do x1 <- handleSignal m1 h- x2 <- handleSignal m2 h- x3 <- handleSignal m3 h- x4 <- handleSignal m4 h- return $ do { x1; x2; x3; x4 },- updateSignal =- do updateSignal m1- updateSignal m2 - updateSignal m3 - updateSignal m4 }- --- | Merge five signals.-merge5Signals :: Signal a -> Signal a -> Signal a -> - Signal a -> Signal a -> Signal a-merge5Signals m1 m2 m3 m4 m5 =- Signal { handleSignal = \h ->- do x1 <- handleSignal m1 h- x2 <- handleSignal m2 h- x3 <- handleSignal m3 h- x4 <- handleSignal m4 h- x5 <- handleSignal m5 h- return $ do { x1; x2; x3; x4; x5 },- updateSignal =- do updateSignal m1- updateSignal m2 - updateSignal m3 - updateSignal m4- updateSignal m5 }---- | Compose the signal.-mapSignalM :: (a -> Dynamics b) -> Signal a -> Signal b-mapSignalM f m =- Signal { handleSignal = \h ->- handleSignal m (f >=> h),- updateSignal = - updateSignal m }- --- | Transform the signal.-apSignal :: Dynamics (a -> b) -> Signal a -> Signal b-apSignal f m =- Signal { handleSignal = \h ->- handleSignal m $ \a -> do { x <- f; h (x a) },- updateSignal =- updateSignal m }---- | An empty signal which is never triggered.-emptySignal :: Signal a-emptySignal =- Signal { handleSignal = \h -> return $ return (),- updateSignal = return () }- -invokeDynamics :: Point -> Dynamics a -> IO a-{-# INLINE invokeDynamics #-}-invokeDynamics p (Dynamics m) = m p
− Simulation/Aivika/Dynamics/Internal/Simulation.hs
@@ -1,202 +0,0 @@--{-# LANGUAGE RecursiveDo #-}---- |--- Module : Simulation.Aivika.Dynamics.Internal.Simulation--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ The module defines the 'Simulation' monad that represents a simulation run.--- -module Simulation.Aivika.Dynamics.Internal.Simulation- (-- * Simulation- Simulation(..),- SimulationLift(..),- Specs(..),- Method(..),- Run(..),- runSimulation,- runSimulations,- -- * Error Handling- catchSimulation,- finallySimulation,- throwSimulation,- -- * Utilities- simulationIndex,- simulationCount,- simulationSpecs) where--import qualified Control.Exception as C-import Control.Exception (IOException, throw, finally)--import Control.Monad-import Control.Monad.Trans-import Control.Monad.Fix------- The Simulation Monad------- | A value in the 'Simulation' monad represents something that--- doesn't change within the simulation run but may change for--- other runs.------ This monad is ideal for representing the external--- parameters for the model, when the Monte-Carlo simulation--- is used. Also this monad is useful for defining some--- actions that should occur only once within the simulation run,--- for example, setting of the integral with help of recursive--- equations.----newtype Simulation a = Simulation (Run -> IO a)---- | It defines the simulation specs.-data Specs = Specs { spcStartTime :: Double, -- ^ the start time- spcStopTime :: Double, -- ^ the stop time- spcDT :: Double, -- ^ the integration time step- spcMethod :: Method -- ^ the integration method- } deriving (Eq, Ord, Show)---- | It defines the integration method.-data Method = Euler -- ^ Euler's method- | RungeKutta2 -- ^ the 2nd order Runge-Kutta method- | RungeKutta4 -- ^ the 4th order Runge-Kutta method- deriving (Eq, Ord, Show)---- | It indentifies the simulation run.-data Run = Run { runSpecs :: Specs, -- ^ the simulation specs- runIndex :: Int, -- ^ the current simulation run index- runCount :: Int -- ^ the total number of runs in this experiment- } deriving (Eq, Ord, Show)--instance Monad Simulation where- return = returnS- m >>= k = bindS m k--returnS :: a -> Simulation a-returnS a = Simulation (\r -> return a)--bindS :: Simulation a -> (a -> Simulation b) -> Simulation b-bindS (Simulation m) k = - Simulation $ \r -> - do a <- m r- let Simulation m' = k a- m' r---- | Run the simulation using the specified specs.-runSimulation :: Simulation a -> Specs -> IO a-runSimulation (Simulation m) sc =- m Run { runSpecs = sc,- runIndex = 1,- runCount = 1 }---- | Run the given number of simulations using the specified specs, --- where each simulation is distinguished by its index 'simulationIndex'.-runSimulations :: Simulation a -> Specs -> Int -> [IO a]-runSimulations (Simulation m) sc runs = map f [1 .. runs]- where f i = m Run { runSpecs = sc,- runIndex = i,- runCount = runs }---- | Return the run index for the current simulation.-simulationIndex :: Simulation Int-simulationIndex = Simulation $ return . runIndex---- | Return the number of simulations currently run.-simulationCount :: Simulation Int-simulationCount = Simulation $ return . runCount---- | Return the simulation specs-simulationSpecs :: Simulation Specs-simulationSpecs = Simulation $ return . runSpecs--instance Functor Simulation where- fmap = liftMS--instance Eq (Simulation a) where- x == y = error "Can't compare simulation runs." --instance Show (Simulation a) where- showsPrec _ x = showString "<< Simulation >>"--liftMS :: (a -> b) -> Simulation a -> Simulation b-{-# INLINE liftMS #-}-liftMS f (Simulation x) =- Simulation $ \r -> do { a <- x r; return $ f a }--liftM2S :: (a -> b -> c) -> Simulation a -> Simulation b -> Simulation c-{-# INLINE liftM2S #-}-liftM2S f (Simulation x) (Simulation y) =- Simulation $ \r -> do { a <- x r; b <- y r; return $ f a b }--instance (Num a) => Num (Simulation a) where- x + y = liftM2S (+) x y- x - y = liftM2S (-) x y- x * y = liftM2S (*) x y- negate = liftMS negate- abs = liftMS abs- signum = liftMS signum- fromInteger i = return $ fromInteger i--instance (Fractional a) => Fractional (Simulation a) where- x / y = liftM2S (/) x y- recip = liftMS recip- fromRational t = return $ fromRational t--instance (Floating a) => Floating (Simulation a) where- pi = return pi- exp = liftMS exp- log = liftMS log- sqrt = liftMS sqrt- x ** y = liftM2S (**) x y- sin = liftMS sin- cos = liftMS cos- tan = liftMS tan- asin = liftMS asin- acos = liftMS acos- atan = liftMS atan- sinh = liftMS sinh- cosh = liftMS cosh- tanh = liftMS tanh- asinh = liftMS asinh- acosh = liftMS acosh- atanh = liftMS atanh--instance MonadIO Simulation where- liftIO m = Simulation $ const m---- | A type class to lift the simulation computations in other monads.-class Monad m => SimulationLift m where- - -- | Lift the specified 'Simulation' computation in another monad.- liftSimulation :: Simulation a -> m a- --- | Exception handling within 'Simulation' computations.-catchSimulation :: Simulation a -> (IOException -> Simulation a) -> Simulation a-catchSimulation (Simulation m) h =- Simulation $ \r -> - C.catch (m r) $ \e ->- let Simulation m' = h e in m' r- --- | A computation with finalization part like the 'finally' function.-finallySimulation :: Simulation a -> Simulation b -> Simulation a-finallySimulation (Simulation m) (Simulation m') =- Simulation $ \r ->- C.finally (m r) (m' r)---- | Like the standard 'throw' function.-throwSimulation :: IOException -> Simulation a-throwSimulation = throw---- | Invoke the 'Simulation' computation.-invokeSimulation :: Simulation a -> Run -> IO a-{-# INLINE invokeSimulation #-}-invokeSimulation (Simulation m) r = m r--instance MonadFix Simulation where- mfix f = - Simulation $ \r ->- do { rec { a <- invokeSimulation (f a) r }; return a }
− Simulation/Aivika/Dynamics/Internal/Time.hs
@@ -1,59 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Internal.Time--- Copyright : Copyright (c) 2009-2013, 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 time parameters.-----module Simulation.Aivika.Dynamics.Internal.Time- (starttime, - stoptime, - dt, - time,- integTimes, - isTimeInteg,- integIteration,- integPhase) where--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics---- | Return the start simulation time.-starttime :: Dynamics Double-starttime = Dynamics $ return . spcStartTime . pointSpecs---- | Return the stop simulation time.-stoptime :: Dynamics Double-stoptime = Dynamics $ return . spcStopTime . pointSpecs---- | Return the integration time step.-dt :: Dynamics Double-dt = Dynamics $ return . spcDT . pointSpecs---- | Return the current simulation time.-time :: Dynamics Double-time = Dynamics $ return . pointTime ---- | Return the integration time points.-integTimes :: Specs -> [Double]-integTimes sc = map t [nl .. nu]- where (nl, nu) = integIterationBnds sc- t n = basicTime sc n 0- --- | Whether the current time is an integration time.-isTimeInteg :: Dynamics Bool-isTimeInteg = Dynamics $ \p -> return $ pointPhase p >= 0---- | Return the integration iteration closest to the current simulation time.-integIteration :: Dynamics Int-integIteration = Dynamics $ return . pointIteration---- | Return the integration phase for the current simulation time.--- It is @(-1)@ for non-integration time points.-integPhase :: Dynamics Int-integPhase = Dynamics $ return . pointPhase
+ Simulation/Aivika/Dynamics/Interpolate.hs view
@@ -0,0 +1,63 @@++-- |+-- Module : Simulation.Aivika.Dynamics.Interpolate+-- Copyright : Copyright (c) 2009-2013, 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 interpolation functions.+-- These functions complement the memoization, possibly except for +-- the 'initDynamics' function which is useful to get an initial +-- value of any dynamic process.+--++module Simulation.Aivika.Dynamics.Interpolate+ (initDynamics,+ discreteDynamics,+ interpolateDynamics) where++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Dynamics++-- | Return the initial value.+initDynamics :: Dynamics a -> Dynamics a+{-# INLINE initDynamics #-}+initDynamics (Dynamics m) =+ Dynamics $ \p ->+ if pointIteration p == 0 && pointPhase p == 0 then+ m p+ else+ let sc = pointSpecs p+ in m $ p { pointTime = basicTime sc 0 0,+ pointIteration = 0,+ pointPhase = 0 } ++-- | Discretize the computation in the integration time points.+discreteDynamics :: Dynamics a -> Dynamics a+{-# INLINE discreteDynamics #-}+discreteDynamics (Dynamics m) =+ Dynamics $ \p ->+ if pointPhase p == 0 then+ m p+ else+ let sc = pointSpecs p+ n = pointIteration p+ in m $ p { pointTime = basicTime sc n 0,+ pointPhase = 0 }++-- | Interpolate the computation based on the integration time points only.+-- Unlike the 'discreteDynamics' function it knows about the intermediate +-- time points that are used in the Runge-Kutta method.+interpolateDynamics :: Dynamics a -> Dynamics a+{-# INLINE interpolateDynamics #-}+interpolateDynamics (Dynamics m) = + Dynamics $ \p -> + if pointPhase p >= 0 then + m p+ else + let sc = pointSpecs p+ n = pointIteration p+ in m $ p { pointTime = basicTime sc n 0,+ pointPhase = 0 }
− Simulation/Aivika/Dynamics/LIFO.hs
@@ -1,196 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.LIFO--- Copyright : Copyright (c) 2009-2013, 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 LIFO queue.----module Simulation.Aivika.Dynamics.LIFO- (LIFO,- lifoQueue,- lifoNull,- lifoFull,- lifoMaxCount,- lifoCount,- lifoLostCount,- lifoEnqueue,- lifoDequeue,- lifoEnqueueLost,- newLIFO,- dequeueLIFO,- tryDequeueLIFO,- enqueueLIFO,- tryEnqueueLIFO,- enqueueLIFOOrLost) where--import Data.IORef-import Data.Array-import Data.Array.IO.Safe--import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Process-import Simulation.Aivika.Dynamics.Resource-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Signal---- | Represents the LIFO queue with rule: last input - first output.-data LIFO a =- LIFO { lifoQueue :: EventQueue, -- ^ Return the event queue.- lifoMaxCount :: Int, -- ^ The maximum available number of items.- lifoReadRes :: Resource,- lifoWriteRes :: Resource,- lifoCountRef :: IORef Int,- lifoLostCountRef :: IORef Int,- lifoArray :: IOArray Int a, - lifoEnqueueSource :: SignalSource a,- lifoEnqueueLostSource :: SignalSource a,- lifoDequeueSource :: SignalSource a,- lifoUpdatedSource :: SignalSource a }- --- | Create a new LIFO queue with the specified maximum available number of items. -newLIFO :: EventQueue -> Int -> Simulation (LIFO a) -newLIFO q count =- do i <- liftIO $ newIORef 0- l <- liftIO $ newIORef 0- a <- liftIO $ newArray_ (0, count - 1)- r <- newResourceWithCount q count 0- w <- newResourceWithCount q count count- s1 <- newSignalSourceUnsafe- s2 <- newSignalSourceUnsafe- s3 <- newSignalSourceUnsafe- s4 <- newSignalSource q- return LIFO { lifoQueue = q,- lifoMaxCount = count,- lifoReadRes = r,- lifoWriteRes = w,- lifoCountRef = i,- lifoLostCountRef = l,- lifoArray = a,- lifoEnqueueSource = s1,- lifoEnqueueLostSource = s2,- lifoDequeueSource = s3,- lifoUpdatedSource = s4 }- --- | Test whether the LIFO queue is empty.-lifoNull :: LIFO a -> Dynamics Bool-lifoNull lifo =- do a <- lifoCount lifo- return (a == 0)---- | Test whether the LIFO queue is full.-lifoFull :: LIFO a -> Dynamics Bool-lifoFull lifo =- do a <- lifoCount lifo- return (a == lifoMaxCount lifo)---- | Return the queue size.-lifoCount :: LIFO a -> Dynamics Int-lifoCount lifo =- liftIO $ readIORef (lifoCountRef lifo)- --- | Return the number of lost items.-lifoLostCount :: LIFO a -> Dynamics Int-lifoLostCount lifo =- liftIO $ readIORef (lifoLostCountRef lifo)- --- | Dequeue from the LIFO queue suspending the process if--- the queue is empty.-dequeueLIFO :: LIFO a -> Process a -dequeueLIFO lifo =- do requestResource (lifoReadRes lifo)- a <- liftIO $ dequeueImpl lifo- releaseResource (lifoWriteRes lifo)- liftDynamics $ triggerSignal (lifoDequeueSource lifo) a- return a- --- | Try to dequeue from the LIFO queue immediately. -tryDequeueLIFO :: LIFO a -> Dynamics (Maybe a)-tryDequeueLIFO lifo =- do x <- tryRequestResourceInDynamics (lifoReadRes lifo)- if x - then do a <- liftIO $ dequeueImpl lifo- releaseResourceInDynamics (lifoWriteRes lifo)- triggerSignal (lifoDequeueSource lifo) a- return $ Just a- else return Nothing---- | Enqueue the item in the LIFO queue suspending the process if--- the queue is full. -enqueueLIFO :: LIFO a -> a -> Process ()-enqueueLIFO lifo a =- do requestResource (lifoWriteRes lifo)- liftIO $ enqueueImpl lifo a- releaseResource (lifoReadRes lifo)- liftDynamics $ triggerSignal (lifoEnqueueSource lifo) a- --- | Try to enqueue the item in the LIFO queue. Return 'False' in--- the monad if the queue is full.-tryEnqueueLIFO :: LIFO a -> a -> Dynamics Bool-tryEnqueueLIFO lifo a =- do x <- tryRequestResourceInDynamics (lifoWriteRes lifo)- if x - then do liftIO $ enqueueImpl lifo a- releaseResourceInDynamics (lifoReadRes lifo)- triggerSignal (lifoEnqueueSource lifo) a- return True- else return False---- | Try to enqueue the item in the LIFO queue. If the queue is full--- then the item will be lost.-enqueueLIFOOrLost :: LIFO a -> a -> Dynamics ()-enqueueLIFOOrLost lifo a =- do x <- tryRequestResourceInDynamics (lifoWriteRes lifo)- if x- then do liftIO $ enqueueImpl lifo a- releaseResourceInDynamics (lifoReadRes lifo)- triggerSignal (lifoEnqueueSource lifo) a- else do liftIO $ modifyIORef (lifoLostCountRef lifo) $ (+) 1- triggerSignal (lifoEnqueueLostSource lifo) a---- | Return a signal that notifies when any item is enqueued.-lifoEnqueue :: LIFO a -> Signal a-lifoEnqueue lifo = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (lifoUpdatedSource lifo)- m2 = publishSignal (lifoEnqueueSource lifo)---- | Return a signal which notifies that the item was lost when--- attempting to add it to the full queue with help of--- 'enqueueLIFOOrLost'.-lifoEnqueueLost :: LIFO a -> Signal a-lifoEnqueueLost lifo = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (lifoUpdatedSource lifo)- m2 = publishSignal (lifoEnqueueLostSource lifo)---- | Return a signal that notifies when any item is dequeued.-lifoDequeue :: LIFO a -> Signal a-lifoDequeue lifo = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (lifoUpdatedSource lifo)- m2 = publishSignal (lifoDequeueSource lifo)----- | An implementation method.-dequeueImpl :: LIFO a -> IO a-dequeueImpl lifo =- do i <- readIORef (lifoCountRef lifo)- let j = i - 1- a <- j `seq` readArray (lifoArray lifo) j- writeArray (lifoArray lifo) j undefined- writeIORef (lifoCountRef lifo) j- return a---- | An implementation method.-enqueueImpl :: LIFO a -> a -> IO ()-enqueueImpl lifo a =- do i <- readIORef (lifoCountRef lifo)- let j = i + 1- a `seq` writeArray (lifoArray lifo) i a- j `seq` writeIORef (lifoCountRef lifo) j
+ Simulation/Aivika/Dynamics/Memo.hs view
@@ -0,0 +1,125 @@++-- |+-- Module : Simulation.Aivika.Dynamics.Memo+-- Copyright : Copyright (c) 2009-2013, 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 memo functions. The memoization creates such 'Dynamics'+-- computations, which values are cached in the integration time points. Then+-- these values are interpolated in all other time points.+--++module Simulation.Aivika.Dynamics.Memo+ (memoDynamics,+ memo0Dynamics,+ iterateDynamics) where++import Data.Array+import Data.Array.IO.Safe+import Data.IORef+import Control.Monad++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics+import Simulation.Aivika.Dynamics.Interpolate++-- | Create a boxed array with default values.+newBoxedArray_ :: Ix i => (i, i) -> IO (IOArray i e)+newBoxedArray_ = newArray_++-- | Memoize and order the computation in the integration time points using +-- the interpolation that knows of the Runge-Kutta method.+memoDynamics :: Dynamics e -> Simulation (Dynamics e)+{-# INLINE memoDynamics #-}+memoDynamics (Dynamics m) = + Simulation $ \r ->+ do let sc = runSpecs r+ (phl, phu) = integPhaseBnds sc+ (nl, nu) = integIterationBnds sc+ arr <- newBoxedArray_ ((phl, nl), (phu, nu))+ nref <- newIORef 0+ phref <- newIORef 0+ let r p = + do let sc = pointSpecs p+ n = pointIteration p+ ph = pointPhase p+ phu = integPhaseHiBnd sc + loop n' ph' = + if (n' > n) || ((n' == n) && (ph' > ph)) + then + readArray arr (ph, n)+ else + let p' = p { pointIteration = n', pointPhase = ph',+ pointTime = basicTime sc n' ph' }+ in do a <- m p'+ a `seq` writeArray arr (ph', n') a+ if ph' >= phu + then do writeIORef phref 0+ writeIORef nref (n' + 1)+ loop (n' + 1) 0+ else do writeIORef phref (ph' + 1)+ loop n' (ph' + 1)+ n' <- readIORef nref+ ph' <- readIORef phref+ loop n' ph'+ return $ interpolateDynamics $ Dynamics r++-- | Memoize and order the computation in the integration time points using +-- the 'discreteDynamics' interpolation. It consumes less memory than the 'memoDynamics'+-- function but it is not aware of the Runge-Kutta method. There is a subtle+-- difference when we request for values in the intermediate time points+-- that are used by this method to integrate. In general case you should +-- prefer the 'memo0Dynamics' function above 'memoDynamics'.+memo0Dynamics :: Dynamics e -> Simulation (Dynamics e)+{-# INLINE memo0Dynamics #-}+memo0Dynamics (Dynamics m) = + Simulation $ \r ->+ do let sc = runSpecs r+ bnds = integIterationBnds sc+ arr <- newBoxedArray_ bnds+ nref <- newIORef 0+ let r p =+ do let sc = pointSpecs p+ n = pointIteration p+ loop n' = + if n' > n+ then + readArray arr n+ else + let p' = p { pointIteration = n', pointPhase = 0,+ pointTime = basicTime sc n' 0 }+ in do a <- m p'+ a `seq` writeArray arr n' a+ writeIORef nref (n' + 1)+ loop (n' + 1)+ n' <- readIORef nref+ loop n'+ return $ discreteDynamics $ Dynamics r++-- | Iterate sequentially the dynamic process with side effects in +-- the integration time points. It is equivalent to a call of the+-- 'memo0Dynamics' function but significantly more efficient, for the array +-- is not created.+iterateDynamics :: Dynamics () -> Simulation (Dynamics ())+{-# INLINE iterateDynamics #-}+iterateDynamics (Dynamics m) = + Simulation $ \r ->+ do let sc = runSpecs r+ nref <- newIORef 0+ let r p =+ do let sc = pointSpecs p+ n = pointIteration p+ loop n' = + unless (n' > n) $+ let p' = p { pointIteration = n', pointPhase = 0,+ pointTime = basicTime sc n' 0 }+ in do a <- m p'+ a `seq` writeIORef nref (n' + 1)+ loop (n' + 1)+ n' <- readIORef nref+ loop n'+ return $ discreteDynamics $ Dynamics r
+ Simulation/Aivika/Dynamics/Memo/Unboxed.hs view
@@ -0,0 +1,100 @@++{-# LANGUAGE FlexibleContexts #-}++-- |+-- Module : Simulation.Aivika.Dynamics.Memo.Unboxed+-- Copyright : Copyright (c) 2009-2013, 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 unboxed memo functions. The memoization creates such 'Dynamics'+-- computations, which values are cached in the integration time points. Then+-- these values are interpolated in all other time points.+--++module Simulation.Aivika.Dynamics.Memo.Unboxed+ (memoDynamics,+ memo0Dynamics) where++import Data.Array+import Data.Array.IO.Safe+import Data.IORef+import Control.Monad++import Simulation.Aivika.Internal.Specs+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.+memoDynamics :: Unboxed e => Dynamics e -> Simulation (Dynamics e)+{-# INLINE memoDynamics #-}+memoDynamics (Dynamics m) = + Simulation $ \r ->+ do let sc = runSpecs r+ (phl, phu) = integPhaseBnds sc+ (nl, nu) = integIterationBnds sc+ arr <- newUnboxedArray_ ((phl, nl), (phu, nu))+ nref <- newIORef 0+ phref <- newIORef 0+ let r p =+ do let sc = pointSpecs p+ n = pointIteration p+ ph = pointPhase p+ phu = integPhaseHiBnd sc + loop n' ph' = + if (n' > n) || ((n' == n) && (ph' > ph)) + then + readArray arr (ph, n)+ else + let p' = p { pointIteration = n', + pointPhase = ph',+ pointTime = basicTime sc n' ph' }+ in do a <- m p'+ a `seq` writeArray arr (ph', n') a+ if ph' >= phu + then do writeIORef phref 0+ writeIORef nref (n' + 1)+ loop (n' + 1) 0+ else do writeIORef phref (ph' + 1)+ loop n' (ph' + 1)+ n' <- readIORef nref+ ph' <- readIORef phref+ loop n' ph'+ return $ interpolateDynamics $ Dynamics r++-- | Memoize and order the computation in the integration time points using +-- the 'discreteDynamics' interpolation. It consumes less memory than the 'memoDynamics'+-- function but it is not aware of the Runge-Kutta method. There is a subtle+-- difference when we request for values in the intermediate time points+-- that are used by this method to integrate. In general case you should +-- prefer the 'memo0Dynamics' function above 'memoDynamics'.+memo0Dynamics :: Unboxed e => Dynamics e -> Simulation (Dynamics e)+{-# INLINE memo0Dynamics #-}+memo0Dynamics (Dynamics m) = + Simulation $ \r ->+ do let sc = runSpecs r+ bnds = integIterationBnds sc+ arr <- newUnboxedArray_ bnds+ nref <- newIORef 0+ let r p =+ do let sc = pointSpecs p+ n = pointIteration p+ loop n' = + if n' > n+ then + readArray arr n+ else + let p' = p { pointIteration = n', pointPhase = 0,+ pointTime = basicTime sc n' 0 }+ in do a <- m p'+ a `seq` writeArray arr n' a+ writeIORef nref (n' + 1)+ loop (n' + 1)+ n' <- readIORef nref+ loop n'+ return $ discreteDynamics $ Dynamics r
− Simulation/Aivika/Dynamics/Parameter.hs
@@ -1,83 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Parameter--- Copyright : Copyright (c) 2009-2013, 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 parameters of simulation experiments.-----module Simulation.Aivika.Dynamics.Parameter- (newParameter,- newTableParameter,- newIndexedParameter,- newRandomParameter,- newNormalParameter) where--import Data.Array-import Data.IORef-import qualified Data.Map as M-import Control.Concurrent.MVar-import System.Random--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Random---- | Create a thread-safe parameter that returns always the same value within the simulation run, --- where the value is recalculated for each new run.-newParameter :: IO a -> IO (Simulation a)-newParameter a = newIndexedParameter $ \_ -> a---- | Create a thread-safe parameter that returns always the same value within the simulation run,--- where the value is taken consequently from the specified table based on the number of the --- current run starting from zero. After all values from the table are used, it takes the first --- value of the table, then the second one and so on.-newTableParameter :: Array Int a -> IO (Simulation a)-newTableParameter t = newIndexedParameter (\i -> return $ t ! (((i - i1) `mod` n) + i1))- where (i1, i2) = bounds t- n = i2 - i1 + 1---- | Create a thread-safe parameter that returns always the same value within the simulation run, --- where the value depends on the number of this run starting from zero.-newIndexedParameter :: (Int -> IO a) -> IO (Simulation a)-newIndexedParameter f = - do lock <- newMVar ()- dict <- newIORef M.empty- return $ Simulation $ \r ->- do let i = runIndex r- m <- readIORef dict- if M.member i m- then do let Just v = M.lookup i m- return v- else withMVar lock $ - \() -> do { m <- readIORef dict;- if M.member i m- then do let Just v = M.lookup i m- return v- else do v <- f i- writeIORef dict $ M.insert i v m- return v }---- | Create a new random parameter distributed uniformly.--- The value doesn't change within the simulation run but--- then the value is recalculated for each new run.-newRandomParameter :: Simulation Double -- ^ minimum- -> Simulation Double -- ^ maximum- -> IO (Simulation Double)-newRandomParameter min max =- do x <- newParameter $ getStdRandom random- return $ min + x * (max - min)---- | Create a new random parameter distributed normally.--- The value doesn't change within the simulation run but--- then the value is recalculated for each new run.-newNormalParameter :: Simulation Double -- ^ mean- -> Simulation Double -- ^ variance- -> IO (Simulation Double)-newNormalParameter mu nu =- do x <- normalGen >>= newParameter- return $ mu + x * nu
− Simulation/Aivika/Dynamics/Process.hs
@@ -1,42 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Process--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ A value in the 'Process' monad represents a discontinuous process that --- can suspend in any simulation time point and then resume later in the same --- or another time point. --- --- The process of this type behaves like a dynamic process too. So, any value --- in the 'Dynamics' monad can be lifted to the Process monad. Moreover, --- a value in the Process monad can be run in the Dynamics monad.------ A value of the 'ProcessID' type is just an identifier of such a process.----module Simulation.Aivika.Dynamics.Process- (ProcessID,- Process,- processQueue,- newProcessID,- newProcessIDWithCatch,- holdProcess,- interruptProcess,- processInterrupted,- passivateProcess,- processPassive,- reactivateProcess,- processID,- cancelProcess,- processCanceled,- runProcess,- runProcessNow,- catchProcess,- finallyProcess,- throwProcess) where--import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Process
Simulation/Aivika/Dynamics/Random.hs view
@@ -7,74 +7,38 @@ -- Stability : experimental -- Tested with: GHC 7.6.3 ----- Below are defined random functions that mostly return discrete processes. --- Literally, it means that the values are initially defined in integration --- time points and then they are passed to the 'discrete' function.+-- Below are defined random functions that return the 'Dynamics' computations. +-- The values are initially defined in the integration time points and then+-- they are passed in to the 'memo0Dynamics' function to memoize and then interpolate. -- module Simulation.Aivika.Dynamics.Random - (newRandom, newNormal, normalGen) where+ (newRandomDynamics, newNormalDynamics) where import System.Random import Data.IORef import Control.Monad.Trans +import Simulation.Aivika.Simulation+import Simulation.Aivika.Random import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base+import Simulation.Aivika.Dynamics.Memo.Unboxed -- | Return the uniform random numbers in the integration time points.-newRandom :: Dynamics Double -- ^ minimum- -> Dynamics Double -- ^ maximum- -> Simulation (Dynamics Double)-newRandom min max =- umemo0 $ do+newRandomDynamics :: Dynamics Double -- ^ minimum+ -> Dynamics Double -- ^ maximum+ -> Simulation (Dynamics Double)+newRandomDynamics min max =+ memo0Dynamics $ do x <- liftIO $ getStdRandom random min + return x * (max - min) -- | Return the normal random numbers in the integration time points.-newNormal :: Dynamics Double -- ^ mean- -> Dynamics Double -- ^ variance- -> Simulation (Dynamics Double)-newNormal mu nu =- do g <- liftIO normalGen- umemo0 $ do+newNormalDynamics :: Dynamics Double -- ^ mean+ -> Dynamics Double -- ^ variance+ -> Simulation (Dynamics Double)+newNormalDynamics mu nu =+ do g <- liftIO newNormalGen+ memo0Dynamics $ do x <- liftIO g mu + return x * nu---- | Normal random number generator with mean 0 and variance 1.-normalGen :: IO (IO Double)-normalGen =- do nextRef <- newIORef 0.0- flagRef <- newIORef False- xi1Ref <- newIORef 0.0- xi2Ref <- newIORef 0.0- psiRef <- newIORef 0.0- let loop =- do psi <- readIORef psiRef- if (psi >= 1.0) || (psi == 0.0)- then do g1 <- getStdRandom random- g2 <- getStdRandom random- let xi1 = 2.0 * g1 - 1.0- xi2 = 2.0 * g2 - 1.0- psi = xi1 * xi1 + xi2 * xi2- writeIORef xi1Ref xi1- writeIORef xi2Ref xi2- writeIORef psiRef psi- loop- else writeIORef psiRef $ sqrt (- 2.0 * log psi / psi)- return $- do flag <- readIORef flagRef- if flag- then do writeIORef flagRef False- readIORef nextRef- else do writeIORef xi1Ref 0.0- writeIORef xi2Ref 0.0- writeIORef psiRef 0.0- loop- xi1 <- readIORef xi1Ref- xi2 <- readIORef xi2Ref- psi <- readIORef psiRef- writeIORef flagRef True- writeIORef nextRef $ xi2 * psi- return $ xi1 * psi
− Simulation/Aivika/Dynamics/Ref.hs
@@ -1,89 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Ref--- Copyright : Copyright (c) 2009-2013, 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 an updatable reference that depends on the event queue.----module Simulation.Aivika.Dynamics.Ref- (Ref,- refQueue,- refChanged,- refChanged_,- newRef,- readRef,- writeRef,- modifyRef) where--import Data.IORef-import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Signal---- | The 'Ref' type represents a mutable variable similar to the 'IORef' variable --- but only bound to some event queue, which makes the variable coordinated --- with that queue.-data Ref a = - Ref { refQueue :: EventQueue, -- ^ Return the bound event queue.- refRun :: Dynamics (),- refValue :: IORef a, - refChangedSource :: SignalSource a, - refUpdatedSource :: SignalSource a }---- | Create a new reference bound to the specified event queue.-newRef :: EventQueue -> a -> Simulation (Ref a)-newRef q a =- do x <- liftIO $ newIORef a- s <- newSignalSourceUnsafe- u <- newSignalSource q- return Ref { refQueue = q,- refRun = runQueueSync q,- refValue = x, - refChangedSource = s, - refUpdatedSource = u }- --- | Read the value of a reference, forcing the bound event queue to raise --- the events in case of need.-readRef :: Ref a -> Dynamics a-readRef r = Dynamics $ \p -> - do invokeDynamics p $ refRun r- readIORef (refValue r)---- | Write a new value into the reference.-writeRef :: Ref a -> a -> Dynamics ()-writeRef r a = Dynamics $ \p -> - do a `seq` writeIORef (refValue r) a- invokeDynamics p $ triggerSignal (refChangedSource r) a---- | Mutate the contents of the reference, forcing the bound event queue to--- raise all pending events in case of need.-modifyRef :: Ref a -> (a -> a) -> Dynamics ()-modifyRef r f = Dynamics $ \p -> - do invokeDynamics p $ refRun r- a <- readIORef (refValue r)- let b = f a- b `seq` writeIORef (refValue r) b- invokeDynamics p $ triggerSignal (refChangedSource r) b---- | Return a signal that notifies about every change of the reference state.-refChanged :: Ref a -> Signal a-refChanged v = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (refUpdatedSource v)- m2 = publishSignal (refChangedSource v)---- | Return a signal that notifies about every change of the reference state.-refChanged_ :: Ref a -> Signal ()-refChanged_ r = mapSignal (const ()) $ refChanged r--invokeDynamics :: Point -> Dynamics a -> IO a-{-# INLINE invokeDynamics #-}-invokeDynamics p (Dynamics m) = m p
− Simulation/Aivika/Dynamics/Resource.hs
@@ -1,171 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Resource--- Copyright : Copyright (c) 2009-2013, 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 a limited resource which can be acquired and --- then released by the discontinuous process 'Process'.----module Simulation.Aivika.Dynamics.Resource- (Resource,- newResource,- newResourceWithCount,- resourceQueue,- resourceInitCount,- resourceCount,- requestResource,- tryRequestResourceInDynamics,- releaseResource,- releaseResourceInDynamics,- usingResource) where--import Data.IORef-import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Cont-import Simulation.Aivika.Dynamics.Internal.Process-import Simulation.Aivika.Dynamics.EventQueue-import qualified Simulation.Aivika.Queue as Q---- | Represents a limited resource.-data Resource = - Resource { resourceQueue :: EventQueue, - -- ^ Return the bound event queue.- resourceInitCount :: Int,- -- ^ Return the initial count of the resource.- resourceCountRef :: IORef Int, - resourceWaitQueue :: Q.Queue (ContParams ())}--instance Eq Resource where- x == y = resourceCountRef x == resourceCountRef y -- unique references---- | Create a new resource with the specified initial count.-newResource :: EventQueue -> Int -> Simulation Resource-newResource q initCount =- Simulation $ \r ->- do countRef <- newIORef initCount- waitQueue <- Q.newQueue- return Resource { resourceQueue = q,- resourceInitCount = initCount,- resourceCountRef = countRef,- resourceWaitQueue = waitQueue }---- | Create a new resource with the specified initial count.--- The third argument specifies how the resource is consumed --- at the beginning, i.e. it defines the current count, which must be --- non-negative and less or equal to the initial count.-newResourceWithCount :: EventQueue -> Int -> Int -> Simulation Resource-newResourceWithCount q initCount count = do- when (count < 0) $- error $- "The resource count cannot be negative: " ++- "newResourceWithCount."- when (count > initCount) $- error $- "The resource count cannot be greater than " ++- "its initial value: newResourceWithCount."- Simulation $ \r ->- do countRef <- newIORef count- waitQueue <- Q.newQueue- return Resource { resourceQueue = q,- resourceInitCount = initCount,- resourceCountRef = countRef,- resourceWaitQueue = waitQueue }---- | Return the current count of the resource.-resourceCount :: Resource -> Dynamics Int-resourceCount r =- Dynamics $ \p ->- do invokeDynamics p $ runQueueSync (resourceQueue r)- readIORef (resourceCountRef r)---- | Request for the resource decreasing its count in case of success,--- otherwise suspending the discontinuous process until some other --- process releases the resource.-requestResource :: Resource -> Process ()-requestResource r =- Process $ \pid ->- Cont $ \c ->- Dynamics $ \p ->- do a <- readIORef (resourceCountRef r)- if a == 0 - then Q.enqueue (resourceWaitQueue r) c- else do let a' = a - 1- a' `seq` writeIORef (resourceCountRef r) a'- invokeDynamics p $ resumeContByParams c ()---- | Release the resource increasing its count and resuming one of the--- previously suspended processes as possible.-releaseResource :: Resource -> Process ()-releaseResource r = - Process $ \_ ->- Cont $ \c ->- Dynamics $ \p ->- do invokeDynamics p $ releaseResourceUnsafe r- invokeDynamics p $ resumeContByParams c ()---- | Release the resource increasing its count and resuming one of the--- previously suspended processes as possible.-releaseResourceInDynamics :: Resource -> Dynamics ()-releaseResourceInDynamics r =- Dynamics $ \p ->- do invokeDynamics p $ runQueueSync (resourceQueue r)- invokeDynamics p $ releaseResourceUnsafe r--releaseResourceUnsafe :: Resource -> Dynamics ()-{-# INLINE releaseResourceUnsafe #-}-releaseResourceUnsafe r =- Dynamics $ \p ->- do a <- readIORef (resourceCountRef r)- let a' = a + 1- when (a' > resourceInitCount r) $- error $- "The resource count cannot be greater than " ++- "its initial value: releaseResourceUnsafe."- f <- Q.queueNull (resourceWaitQueue r)- if f - then a' `seq` writeIORef (resourceCountRef r) a'- else do c <- Q.queueFront (resourceWaitQueue r)- Q.dequeue (resourceWaitQueue r)- invokeDynamics p $ enqueue (resourceQueue r) (pointTime p) $- Dynamics $ \p ->- do z <- contParamsCanceled c- if z- then do invokeDynamics p $ releaseResourceUnsafe r- invokeDynamics p $ resumeContByParams c ()- else invokeDynamics p $ resumeContByParams c ()---- | Try to request for the resource decreasing its count in case of success--- and returning 'True' in the 'Dynamics' monad; otherwise, returning 'False'.-tryRequestResourceInDynamics :: Resource -> Dynamics Bool-tryRequestResourceInDynamics r =- Dynamics $ \p ->- do invokeDynamics p $ runQueueSync (resourceQueue r)- a <- readIORef (resourceCountRef r)- if a == 0 - then return False- else do let a' = a - 1- a' `seq` writeIORef (resourceCountRef r) a'- return True- --- | Acquire the resource, perform some action and safely release the resource --- in the end, even if the 'IOException' was raised within the action. --- The process identifier must be created with support of exception --- handling, i.e. with help of function 'newProcessIDWithCatch'. Unfortunately,--- such processes are slower than those that are created with help of--- other function 'newProcessID'.-usingResource :: Resource -> Process a -> Process a-usingResource r m =- do requestResource r- finallyProcess m $ releaseResource r--invokeDynamics :: Point -> Dynamics a -> IO a-{-# INLINE invokeDynamics #-}-invokeDynamics p (Dynamics m) = m p
− Simulation/Aivika/Dynamics/Signal.hs
@@ -1,184 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Signal--- Copyright : Copyright (c) 2009-2013, 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 signal which we can subscribe handlers to. --- These handlers can be disposed. The signal is triggered in the --- current time point actuating the corresponded computations from --- the handlers. ----module Simulation.Aivika.Dynamics.Signal- (Signal,- SignalSource,- newSignalSource,- newSignalSourceWithUpdate,- newSignalInTimes,- newSignalInIntegTimes,- newSignalInStartTime,- newSignalInStopTime,- publishSignal,- triggerSignal,- handleSignal,- handleSignal_,- updateSignal,- awaitSignal,- mapSignal,- mapSignalM,- apSignal,- filterSignal,- filterSignalM,- emptySignal,- merge2Signals,- merge3Signals,- merge4Signals,- merge5Signals,- SignalHistory,- signalHistorySignal,- newSignalHistory,- newSignalHistoryThrough,- readSignalHistory) where--import Data.IORef-import Data.Array--import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Internal.Cont-import Simulation.Aivika.Dynamics.Internal.Process-import Simulation.Aivika.Dynamics.Base--import qualified Simulation.Aivika.Vector as V-import qualified Simulation.Aivika.UVector as UV---- | Create a new signal source when the state depends on the event queue.------ Since version 0.6.1 its 'updateSignal' function calls 'runQueueSyncBefore'--- instead of 'runQueueSync' as it was before. In case of need you can--- define your own update function with help of 'newSignalSourceWithUpdate'.------ The function has the following defintion:------ @--- newSignalSource queue = --- newSignalSourceWithUpdate $ runQueueSyncBefore queue--- @-newSignalSource :: EventQueue -> Simulation (SignalSource a)-newSignalSource queue = - newSignalSourceWithUpdate $ runQueueSyncBefore queue---- | Await the signal.-awaitSignal :: Signal a -> Process a-awaitSignal signal =- Process $ \pid ->- Cont $ \c ->- Dynamics $ \p ->- do r <- newIORef Nothing- let Dynamics m = - handleSignal signal $ - \a -> Dynamics $ - \p -> do x <- readIORef r- case x of- Nothing ->- error "The signal was lost: awaitSignal."- Just x ->- do let Dynamics m = x- m p- let Dynamics m = resumeContByParams c a- m p- h <- m p- writeIORef r $ Just h- --- | Represents the history of the signal values.-data SignalHistory a =- SignalHistory { signalHistorySignal :: Signal a, - -- ^ The signal for which the history is created.- signalHistoryTimes :: UV.UVector Double,- signalHistoryValues :: V.Vector a }---- | Create a history of the signal values.-newSignalHistory :: Signal a -> Dynamics (SignalHistory a)-newSignalHistory signal =- do ts <- liftIO UV.newVector- xs <- liftIO V.newVector- handleSignal_ signal $ \a ->- Dynamics $ \p ->- do liftIO $ UV.appendVector ts (pointTime p)- liftIO $ V.appendVector xs a- return SignalHistory { signalHistorySignal = signal,- signalHistoryTimes = ts,- signalHistoryValues = xs }- --- | Create a history of the signal values with delay through the event queue.--- The history will be created at the same simulation time, just the corresponded --- handler will be subscribed to the signal after the new event will be processed --- by the queue. ------ Since version 0.6.1, this function has less meaning than before. Please use--- carefully as the behavior depends on the state of the event queue.-newSignalHistoryThrough :: EventQueue -> Signal a -> Dynamics (SignalHistory a)-newSignalHistoryThrough q signal =- do ts <- liftIO UV.newVector- xs <- liftIO V.newVector- enqueueWithCurrentTime q $- handleSignal_ signal $ \a ->- Dynamics $ \p ->- do liftIO $ UV.appendVector ts (pointTime p)- liftIO $ V.appendVector xs a- return SignalHistory { signalHistorySignal = signal,- signalHistoryTimes = ts,- signalHistoryValues = xs }- --- | Read the history of signal values.-readSignalHistory :: SignalHistory a -> Dynamics (Array Int Double, Array Int a)-readSignalHistory history =- do updateSignal $ signalHistorySignal history- xs <- liftIO $ UV.freezeVector (signalHistoryTimes history)- ys <- liftIO $ V.freezeVector (signalHistoryValues history)- return (xs, ys) - --- | Trigger the signal with the current time.-triggerSignalWithTime :: SignalSource Double -> Dynamics ()-triggerSignalWithTime s =- Dynamics $ \p ->- do let Dynamics m = triggerSignal s (pointTime p)- m p---- | Return a signal that is triggered in the specified time points.-newSignalInTimes :: EventQueue -> [Double] -> Dynamics (Signal Double)-newSignalInTimes q xs =- do s <- liftSimulation $ newSignalSource q- enqueueWithTimes q xs $ triggerSignalWithTime s- return $ publishSignal s- --- | Return a signal that is triggered in the integration time points.--- It should be called with help of 'runDynamicsInStartTime'.-newSignalInIntegTimes :: EventQueue -> Dynamics (Signal Double)-newSignalInIntegTimes q =- do s <- liftSimulation $ newSignalSource q- enqueueWithIntegTimes q $ triggerSignalWithTime s- return $ publishSignal s- --- | Return a signal that is triggered in the start time.--- It should be called with help of 'runDynamicsInStartTime'.-newSignalInStartTime :: EventQueue -> Dynamics (Signal Double)-newSignalInStartTime q =- do s <- liftSimulation $ newSignalSource q- enqueueWithStartTime q $ triggerSignalWithTime s- return $ publishSignal s---- | Return a signal that is triggered in the stop time.-newSignalInStopTime :: EventQueue -> Dynamics (Signal Double)-newSignalInStopTime q =- do s <- liftSimulation $ newSignalSource q- enqueueWithStopTime q $ triggerSignalWithTime s- return $ publishSignal s
− Simulation/Aivika/Dynamics/Simulation.hs
@@ -1,29 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Simulation--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ The module defines the 'Simulation' monad representing a simulation run.----module Simulation.Aivika.Dynamics.Simulation- (-- * Simulation- Simulation,- SimulationLift(..),- Specs(..),- Method(..),- runSimulation,- runSimulations,- -- * Error Handling- catchSimulation,- finallySimulation,- throwSimulation,- -- * Utilities- simulationIndex,- simulationCount,- simulationSpecs) where--import Simulation.Aivika.Dynamics.Internal.Simulation
− Simulation/Aivika/Dynamics/SystemDynamics.hs
@@ -1,788 +0,0 @@--{-# LANGUAGE FlexibleContexts, BangPatterns, RecursiveDo #-}---- |--- Module : Simulation.Aivika.Dynamics.SystemDynamics--- Copyright : Copyright (c) 2009-2013, 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 integrals and other functions of System Dynamics.-----module Simulation.Aivika.Dynamics.SystemDynamics- (-- * Equality and Ordering- (.==.),- (./=.),- (.<.),- (.>=.),- (.>.),- (.<=.),- maxDynamics,- minDynamics,- ifDynamics,- -- * Integrals- Integ,- newInteg,- integInit,- integValue,- integDiff,- -- * Integral Functions- integ,- smoothI,- smooth,- smooth3I,- smooth3,- smoothNI,- smoothN,- delay1I,- delay1,- delay3I,- delay3,- delayNI,- delayN,- forecast,- trend,- -- * Difference Equations- Sum,- newSum,- sumInit,- sumValue,- sumDiff,- sumDynamics,- -- * Table Functions- lookupD,- lookupStepwiseD,- lookupDynamics,- lookupStepwiseDynamics,- -- * Discrete Functions- delayTrans,- delay,- delayI,- udelay,- udelayI,- -- * Financial Functions- npv,- npve) where--import Data.Array-import Data.Array.IO.Safe-import Data.IORef-import Control.Monad-import Control.Monad.Trans--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.Base------- Equality and Ordering------- | Compare for equality.-(.==.) :: (Eq a) => Dynamics a -> Dynamics a -> Dynamics Bool-(.==.) = liftM2 (==)---- | Compare for inequality.-(./=.) :: (Eq a) => Dynamics a -> Dynamics a -> Dynamics Bool-(./=.) = liftM2 (/=)---- | Compare for ordering.-(.<.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool-(.<.) = liftM2 (<)---- | Compare for ordering.-(.>=.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool-(.>=.) = liftM2 (>=)---- | Compare for ordering.-(.>.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool-(.>.) = liftM2 (>)---- | Compare for ordering.-(.<=.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool-(.<=.) = liftM2 (<=)---- | Return the maximum.-maxDynamics :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics a-maxDynamics = liftM2 max---- | Return the minimum.-minDynamics :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics a-minDynamics = liftM2 min---- | Implement the if-then-else operator.-ifDynamics :: Dynamics Bool -> Dynamics a -> Dynamics a -> Dynamics a-ifDynamics cond x y =- do a <- cond- if a then x else y------- Integrals-----{-# DEPRECATED Integ "Use the integ function instead" #-}-{-# DEPRECATED newInteg "Use the integ function instead" #-}-{-# DEPRECATED integInit "Use the integ function instead" #-}-{-# DEPRECATED integValue "Use the integ function instead" #-}-{-# DEPRECATED integDiff "Use the integ function instead" #-}---- | The 'Integ' type represents an integral.-data Integ = Integ { integInit :: Dynamics Double, -- ^ The initial value.- integExternal :: IORef (Dynamics Double),- integInternal :: IORef (Dynamics Double) }---- | Create a new integral with the specified initial value.-newInteg :: Dynamics Double -> Simulation Integ-newInteg i = - do r1 <- liftIO $ newIORef $ initDynamics i - r2 <- liftIO $ newIORef $ initDynamics i - let integ = Integ { integInit = i, - integExternal = r1,- integInternal = r2 }- z = Dynamics $ \p -> - do (Dynamics m) <- readIORef (integInternal integ)- m p- y <- umemo z- liftIO $ writeIORef (integExternal integ) y- return integ---- | Return the integral's value.-integValue :: Integ -> Dynamics Double-integValue integ = - Dynamics $ \p ->- do (Dynamics m) <- readIORef (integExternal integ)- m p---- | Set the derivative for the integral.-integDiff :: Integ -> Dynamics Double -> Simulation ()-integDiff integ diff =- do let z = Dynamics $ \p ->- do y <- readIORef (integExternal integ)- let i = integInit integ- case spcMethod (pointSpecs p) of- Euler -> integEuler diff i y p- RungeKutta2 -> integRK2 diff i y p- RungeKutta4 -> integRK4 diff i y p- liftIO $ writeIORef (integInternal integ) z--integEuler :: Dynamics Double- -> Dynamics Double - -> Dynamics Double - -> Point -> IO Double-integEuler (Dynamics f) (Dynamics i) (Dynamics y) p = - case pointIteration p of- 0 -> - i p- n -> do - let sc = pointSpecs p- ty = basicTime sc (n - 1) 0- py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }- a <- y py- b <- f py- let !v = a + spcDT (pointSpecs p) * b- return v--integRK2 :: Dynamics Double- -> Dynamics Double- -> Dynamics Double- -> Point -> IO Double-integRK2 (Dynamics f) (Dynamics i) (Dynamics y) p =- case pointPhase p of- 0 -> case pointIteration p of- 0 ->- i p- n -> do- let sc = pointSpecs p- ty = basicTime sc (n - 1) 0- t1 = ty- t2 = basicTime sc (n - 1) 1- py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }- p1 = py- p2 = p { pointTime = t2, pointIteration = n - 1, pointPhase = 1 }- vy <- y py- k1 <- f p1- k2 <- f p2- let !v = vy + spcDT sc / 2.0 * (k1 + k2)- return v- 1 -> do- let sc = pointSpecs p- n = pointIteration p- ty = basicTime sc n 0- t1 = ty- py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }- p1 = py- vy <- y py- k1 <- f p1- let !v = vy + spcDT sc * k1- return v- _ -> - error "Incorrect phase: integRK2"--integRK4 :: Dynamics Double- -> Dynamics Double- -> Dynamics Double- -> Point -> IO Double-integRK4 (Dynamics f) (Dynamics i) (Dynamics y) p =- case pointPhase p of- 0 -> case pointIteration p of- 0 -> - i p- n -> do- let sc = pointSpecs p- ty = basicTime sc (n - 1) 0- t1 = ty- t2 = basicTime sc (n - 1) 1- t3 = basicTime sc (n - 1) 2- t4 = basicTime sc (n - 1) 3- py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }- p1 = py- p2 = p { pointTime = t2, pointIteration = n - 1, pointPhase = 1 }- p3 = p { pointTime = t3, pointIteration = n - 1, pointPhase = 2 }- p4 = p { pointTime = t4, pointIteration = n - 1, pointPhase = 3 }- vy <- y py- k1 <- f p1- k2 <- f p2- k3 <- f p3- k4 <- f p4- let !v = vy + spcDT sc / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4)- return v- 1 -> do- let sc = pointSpecs p- n = pointIteration p- ty = basicTime sc n 0- t1 = ty- py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }- p1 = py- vy <- y py- k1 <- f p1- let !v = vy + spcDT sc / 2.0 * k1- return v- 2 -> do- let sc = pointSpecs p- n = pointIteration p- ty = basicTime sc n 0- t2 = basicTime sc n 1- py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }- p2 = p { pointTime = t2, pointIteration = n, pointPhase = 1 }- vy <- y py- k2 <- f p2- let !v = vy + spcDT sc / 2.0 * k2- return v- 3 -> do- let sc = pointSpecs p- n = pointIteration p- ty = basicTime sc n 0- t3 = basicTime sc n 2- py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }- p3 = p { pointTime = t3, pointIteration = n, pointPhase = 2 }- vy <- y py- k3 <- f p3- let !v = vy + spcDT sc * k3- return v- _ -> - error "Incorrect phase: integRK4"---- | Return an integral with the specified derivative and initial value.------ To create a loopback, you should use the recursive do-notation.--- It allows defining the differential equations unordered as--- in mathematics:------ @--- model :: Simulation [Double]--- model = --- mdo a <- integ (- ka * a) 100--- b <- integ (ka * a - kb * b) 0--- c <- integ (kb * b) 0--- let ka = 1--- kb = 1--- runDynamicsInStopTime $ sequence [a, b, c]--- @-integ :: Dynamics Double -- ^ the derivative- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the integral-integ diff i =- mdo y <- umemo z- z <- Simulation $ \r ->- case spcMethod (runSpecs r) of- Euler -> return $ Dynamics $ integEuler diff i y- RungeKutta2 -> return $ Dynamics $ integRK2 diff i y- RungeKutta4 -> return $ Dynamics $ integRK4 diff i y- return y---- | Return the first order exponential smooth.------ To create a loopback, you should use the recursive do-notation--- with help of which the function itself is defined:------ @--- smoothI x t i =--- mdo y <- integ ((x - y) \/ t) i--- return y--- @ -smoothI :: Dynamics Double -- ^ the value to smooth over time- -> Dynamics Double -- ^ time- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the first order exponential smooth-smoothI x t i =- mdo y <- integ ((x - y) / t) i- return y---- | Return the first order exponential smooth.------ This is a simplified version of the 'smoothI' function--- without specifing the initial value.-smooth :: Dynamics Double -- ^ the value to smooth over time- -> Dynamics Double -- ^ time- -> Simulation (Dynamics Double) -- ^ the first order exponential smooth-smooth x t = smoothI x t x---- | Return the third order exponential smooth.------ To create a loopback, you should use the recursive do-notation--- with help of which the function itself is defined:------ @--- smooth3I x t i =--- mdo y <- integ ((s2 - y) \/ t') i--- s2 <- integ ((s1 - s2) \/ t') i--- s1 <- integ ((x - s1) \/ t') i--- let t' = t \/ 3.0--- return y--- @ -smooth3I :: Dynamics Double -- ^ the value to smooth over time- -> Dynamics Double -- ^ time- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the third order exponential smooth-smooth3I x t i =- mdo y <- integ ((s2 - y) / t') i- s2 <- integ ((s1 - s2) / t') i- s1 <- integ ((x - s1) / t') i- let t' = t / 3.0- return y---- | Return the third order exponential smooth.--- --- This is a simplified version of the 'smooth3I' function--- without specifying the initial value.-smooth3 :: Dynamics Double -- ^ the value to smooth over time- -> Dynamics Double -- ^ time- -> Simulation (Dynamics Double) -- ^ the third order exponential smooth-smooth3 x t = smooth3I x t x---- | Return the n'th order exponential smooth.------ The result is not discrete in that sense that it may change within the integration time--- interval depending on the integration method used. Probably, you should apply--- the 'discrete' function to the result if you want to achieve an effect when the value is--- not changed within the time interval, which is used sometimes.-smoothNI :: Dynamics Double -- ^ the value to smooth over time- -> Dynamics Double -- ^ time- -> Int -- ^ the order- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the n'th order exponential smooth-smoothNI x t n i =- mdo s <- forM [1 .. n] $ \k ->- if k == 1- then integ ((x - a ! 1) / t') i- else integ ((a ! (k - 1) - a ! k) / t') i- let a = listArray (1, n) s - t' = t / fromIntegral n- return $ a ! n---- | Return the n'th order exponential smooth.------ This is a simplified version of the 'smoothNI' function--- without specifying the initial value.-smoothN :: Dynamics Double -- ^ the value to smooth over time- -> Dynamics Double -- ^ time- -> Int -- ^ the order- -> Simulation (Dynamics Double) -- ^ the n'th order exponential smooth-smoothN x t n = smoothNI x t n x---- | Return the first order exponential delay.------ To create a loopback, you should use the recursive do-notation--- with help of which the function itself is defined:------ @--- delay1I x t i =--- mdo y <- integ (x - y \/ t) (i * t)--- return $ y \/ t--- @ -delay1I :: Dynamics Double -- ^ the value to conserve- -> Dynamics Double -- ^ time- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the first order exponential delay-delay1I x t i =- mdo y <- integ (x - y / t) (i * t)- return $ y / t---- | Return the first order exponential delay.------ This is a simplified version of the 'delay1I' function--- without specifying the initial value.-delay1 :: Dynamics Double -- ^ the value to conserve- -> Dynamics Double -- ^ time- -> Simulation (Dynamics Double) -- ^ the first order exponential delay-delay1 x t = delay1I x t x---- | Return the third order exponential delay.-delay3I :: Dynamics Double -- ^ the value to conserve- -> Dynamics Double -- ^ time- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the third order exponential delay-delay3I x t i =- mdo y <- integ (s2 / t' - y / t') (i * t')- s2 <- integ (s1 / t' - s2 / t') (i * t')- s1 <- integ (x - s1 / t') (i * t')- let t' = t / 3.0- return $ y / t' ---- | Return the third order exponential delay.------ This is a simplified version of the 'delay3I' function--- without specifying the initial value.-delay3 :: Dynamics Double -- ^ the value to conserve- -> Dynamics Double -- ^ time- -> Simulation (Dynamics Double) -- ^ the third order exponential delay-delay3 x t = delay3I x t x---- | Return the n'th order exponential delay.-delayNI :: Dynamics Double -- ^ the value to conserve- -> Dynamics Double -- ^ time- -> Int -- ^ the order- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the n'th order exponential delay-delayNI x t n i =- mdo s <- forM [1 .. n] $ \k ->- if k == 1- then integ (x - (a ! 1) / t') (i * t')- else integ ((a ! (k - 1)) / t' - (a ! k) / t') (i * t')- let a = listArray (1, n) s- t' = t / fromIntegral n- return $ (a ! n) / t'---- | Return the n'th order exponential delay.------ This is a simplified version of the 'delayNI' function--- without specifying the initial value.-delayN :: Dynamics Double -- ^ the value to conserve- -> Dynamics Double -- ^ time- -> Int -- ^ the order- -> Simulation (Dynamics Double) -- ^ the n'th order exponential delay-delayN x t n = delayNI x t n x---- | Return the forecast.------ The function has the following definition:------ @--- forecast x at hz =--- do y <- smooth x at--- return $ x * (1.0 + (x \/ y - 1.0) \/ at * hz)--- @-forecast :: Dynamics Double -- ^ the value to forecast- -> Dynamics Double -- ^ the average time- -> Dynamics Double -- ^ the time horizon- -> Simulation (Dynamics Double) -- ^ the forecast-forecast x at hz =- do y <- smooth x at- return $ x * (1.0 + (x / y - 1.0) / at * hz)---- | Return the trend.------ The function has the following definition:------ @--- trend x at i =--- do y <- smoothI x at (x \/ (1.0 + i * at))--- return $ (x \/ y - 1.0) \/ at--- @-trend :: Dynamics Double -- ^ the value for which the trend is calculated- -> Dynamics Double -- ^ the average time- -> Dynamics Double -- ^ the initial value- -> Simulation (Dynamics Double) -- ^ the fractional change rate-trend x at i =- do y <- smoothI x at (x / (1.0 + i * at))- return $ (x / y - 1.0) / at------- Difference Equations-----{-# DEPRECATED Sum "Use the sumDynamics function instead" #-}-{-# DEPRECATED newSum "Use the sumDynamics function instead" #-}-{-# DEPRECATED sumInit "Use the sumDynamics function instead" #-}-{-# DEPRECATED sumValue "Use the sumDynamics function instead" #-}-{-# DEPRECATED sumDiff "Use the sumDynamics function instead" #-}---- | The 'Sum' type represents a sum defined by some difference equation.-data Sum a = Sum { sumInit :: Dynamics a, -- ^ The initial value.- sumExternal :: IORef (Dynamics a),- sumInternal :: IORef (Dynamics a) }---- | Create a new sum with the specified initial value.-newSum :: (MArray IOUArray a IO, Num a) => Dynamics a -> Simulation (Sum a)-newSum i = - do r1 <- liftIO $ newIORef $ initDynamics i - r2 <- liftIO $ newIORef $ initDynamics i - let sum = Sum { sumInit = i, - sumExternal = r1,- sumInternal = r2 }- z = Dynamics $ \p -> - do (Dynamics m) <- readIORef (sumInternal sum)- m p- y <- umemo0 z- liftIO $ writeIORef (sumExternal sum) y- return sum---- | Return the total sum defined by the difference equation.-sumValue :: Sum a -> Dynamics a-sumValue sum = - Dynamics $ \p ->- do (Dynamics m) <- readIORef (sumExternal sum)- m p---- | Set the difference equation for the sum.-sumDiff :: (MArray IOUArray a IO, Num a) => Sum a -> Dynamics a -> Simulation ()-sumDiff sum (Dynamics diff) =- do let z = Dynamics $ \p ->- case pointIteration p of- 0 -> do- let Dynamics i = sumInit sum- i p- n -> do - Dynamics y <- readIORef (sumExternal sum)- let sc = pointSpecs p- ty = basicTime sc (n - 1) 0- py = p { pointTime = ty, - pointIteration = n - 1, - pointPhase = 0 }- a <- y py- b <- diff py- let !v = a + b- return v- liftIO $ writeIORef (sumInternal sum) z---- | Retun the sum for the difference equation.--- It is like an integral returned by the 'integ' function, only now--- the difference is used instead of derivative.------ As usual, to create a loopback, you should use the recursive do-notation.-sumDynamics :: (MArray IOUArray a IO, Num a)- => Dynamics a -- ^ the difference- -> Dynamics a -- ^ the initial value- -> Simulation (Dynamics a) -- ^ the sum-sumDynamics (Dynamics diff) (Dynamics i) =- mdo y <- umemo z- z <- Simulation $ \r ->- return $ Dynamics $ \p ->- case pointIteration p of- 0 -> i p- n -> do - let Dynamics m = y- sc = pointSpecs p- ty = basicTime sc (n - 1) 0- py = p { pointTime = ty, - pointIteration = n - 1, - pointPhase = 0 }- a <- m py- b <- diff py- let !v = a + b- return v- return y------- Table Functions-----{-# DEPRECATED lookupD "Use the lookupDynamics function instead" #-}-{-# DEPRECATED lookupStepwiseD "Use the lookupStepwiseDynamics function instead" #-}---- | Lookup @x@ in a table of pairs @(x, y)@ using linear interpolation.-lookupD :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double-lookupD = lookupDynamics---- | Lookup @x@ in a table of pairs @(x, y)@ using stepwise function.-lookupStepwiseD :: Dynamics Double- -> Array Int (Double, Double)- -> Dynamics Double-lookupStepwiseD = lookupStepwiseDynamics---- | Lookup @x@ in a table of pairs @(x, y)@ using linear interpolation.-lookupDynamics :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double-lookupDynamics (Dynamics m) tbl =- Dynamics (\p -> do a <- m p; return $ find first last a) where- (first, last) = bounds tbl- find left right x =- if left > right then- error "Incorrect index: table"- else- let index = (left + 1 + right) `div` 2- x1 = fst $ tbl ! index- in if x1 <= x then - let y | index < right = find index right x- | right == last = snd $ tbl ! right- | otherwise = - let x2 = fst $ tbl ! (index + 1)- y1 = snd $ tbl ! index- y2 = snd $ tbl ! (index + 1)- in y1 + (y2 - y1) * (x - x1) / (x2 - x1) - in y- else- let y | left < index = find left (index - 1) x- | left == first = snd $ tbl ! left- | otherwise = error "Incorrect index: table"- in y---- | Lookup @x@ in a table of pairs @(x, y)@ using stepwise function.-lookupStepwiseDynamics :: Dynamics Double- -> Array Int (Double, Double)- -> Dynamics Double-lookupStepwiseDynamics (Dynamics m) tbl =- Dynamics (\p -> do a <- m p; return $ find first last a) where- (first, last) = bounds tbl- find left right x =- if left > right then- error "Incorrect index: table"- else- let index = (left + 1 + right) `div` 2- x1 = fst $ tbl ! index- in if x1 <= x then - let y | index < right = find index right x- | right == last = snd $ tbl ! right- | otherwise = snd $ tbl ! right- in y- else- let y | left < index = find left (index - 1) x- | left == first = snd $ tbl ! left- | otherwise = error "Incorrect index: table"- in y------- Discrete Functions------- | Return the delayed value. This is a general version using the specified transform,--- usually a memoization.-delayTrans :: Dynamics a -- ^ the value to delay- -> Dynamics Double -- ^ the lag time- -> Dynamics a -- ^ the initial value- -> (Dynamics a -> Simulation (Dynamics a)) -- ^ the transform (usually, a memoization)- -> Simulation (Dynamics a) -- ^ the delayed value-delayTrans (Dynamics x) (Dynamics d) (Dynamics i) tr = tr $ Dynamics r - where- r p = do - let t = pointTime p- sc = pointSpecs p- n = pointIteration p- a <- d p- let t' = t - a- n' = fromIntegral $ floor $ (t' - spcStartTime sc) / spcDT sc- y | n' < 0 = i $ p { pointTime = spcStartTime sc,- pointIteration = 0, - pointPhase = 0 }- | n' < n = x $ p { pointTime = t',- pointIteration = n',- pointPhase = -1 }- | n' > n = error $- "Cannot return the future data: delayTrans. " ++- "The lag time cannot be negative."- | otherwise = error $- "Cannot return the current data: delayTrans. " ++- "The lag time is too small."- y---- | Return the delayed value.------ It is defined in the following way:------ @ delay x d = delayTrans x d x memo0 @-delay :: Dynamics a -- ^ the value to delay- -> Dynamics Double -- ^ the lag time- -> Simulation (Dynamics a) -- ^ the delayed value-delay x d = delayTrans x d x memo0---- | Return the delayed value.------ It is defined in the following way:------ @ delayI x d i = delayTrans x d i memo0 @-delayI :: Dynamics a -- ^ the value to delay- -> Dynamics Double -- ^ the lag time- -> Dynamics a -- ^ the initial value- -> Simulation (Dynamics a) -- ^ the delayed value-delayI x d i = delayTrans x d i memo0---- | Return the delayed value. This is a more efficient unboxed version of the 'delay' function.------ It is defined in the following way:------ @ udelay x d = delayTrans x d x umemo0 @-udelay :: (MArray IOUArray a IO, Num a)- => Dynamics a -- ^ the value to delay- -> Dynamics Double -- ^ the lag time- -> Simulation (Dynamics a) -- ^ the delayed value-udelay x d = delayTrans x d x umemo0---- | Return the delayed value. This is a more efficient unboxed version of the 'delayI' function.------ It is defined in the following way:------ @ udelayI x d i = delayTrans x d i umemo0 @-udelayI :: (MArray IOUArray a IO, Num a)- => Dynamics a -- ^ the value to delay- -> Dynamics Double -- ^ the lag time- -> Dynamics a -- ^ the initial value- -> Simulation (Dynamics a) -- ^ the delayed value-udelayI x d i = delayTrans x d i umemo0------- Financial Functions------- | Return the Net Present Value (NPV) of the stream computed using the specified--- discount rate, the initial value and some factor (usually 1).------ It is defined in the following way:------ @--- npv stream rate init factor =--- mdo df <- integ (- df * rate) 1--- accum <- integ (stream * df) init--- return $ (accum + dt * stream * df) * factor--- @-npv :: Dynamics Double -- ^ the stream- -> Dynamics Double -- ^ the discount rate- -> Dynamics Double -- ^ the initial value- -> Dynamics Double -- ^ factor- -> Simulation (Dynamics Double) -- ^ the Net Present Value (NPV)-npv stream rate init factor =- mdo df <- integ (- df * rate) 1- accum <- integ (stream * df) init- return $ (accum + dt * stream * df) * factor---- | Return the Net Present Value End of period (NPVE) of the stream computed--- using the specified discount rate, the initial value and some factor.------ It is defined in the following way:------ @--- npve stream rate init factor =--- mdo df <- integ (- df * rate \/ (1 + rate * dt)) (1 \/ (1 + rate * dt))--- accum <- integ (stream * df) init--- return $ (accum + dt * stream * df) * factor--- @-npve :: Dynamics Double -- ^ the stream- -> Dynamics Double -- ^ the discount rate- -> Dynamics Double -- ^ the initial value- -> Dynamics Double -- ^ factor- -> Simulation (Dynamics Double) -- ^ the Net Present Value End (NPVE)-npve stream rate init factor =- mdo df <- integ (- df * rate / (1 + rate * dt)) (1 / (1 + rate * dt))- accum <- integ (stream * df) init- return $ (accum + dt * stream * df) * factor
− Simulation/Aivika/Dynamics/UVar.hs
@@ -1,164 +0,0 @@--{-# LANGUAGE FlexibleContexts #-}---- |--- Module : Simulation.Aivika.Dynamics.UVar--- Copyright : Copyright (c) 2009-2013, 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 a variable that is bound to the event queue and --- that keeps the history of changes storing the values in an unboxed array.----module Simulation.Aivika.Dynamics.UVar- (UVar,- uvarQueue,- uvarChanged,- uvarChanged_,- newUVar,- readUVar,- writeUVar,- modifyUVar,- freezeUVar) where--import Control.Monad-import Data.Array-import Data.Array.IO.Safe-import Data.IORef--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Signal--import qualified Simulation.Aivika.UVector as UV---- | A version of the 'Var' type which uses an unboxed array to store the values --- in time points. You should prefer this type whenever possible.-data UVar a = - UVar { uvarQueue :: EventQueue, -- ^ Return the bound event queue.- uvarRun :: Dynamics (),- uvarXS :: UV.UVector Double, - uvarYS :: UV.UVector a,- uvarChangedSource :: SignalSource a, - uvarUpdatedSource :: SignalSource a }- --- | Create a new variable bound to the specified event queue.-newUVar :: (MArray IOUArray a IO) => EventQueue -> a -> Simulation (UVar a)-newUVar q a =- Simulation $ \r ->- do xs <- UV.newVector- ys <- UV.newVector- UV.appendVector xs $ spcStartTime $ runSpecs r- UV.appendVector ys a- s <- invokeSimulation r newSignalSourceUnsafe- u <- invokeSimulation r $ newSignalSource q- return UVar { uvarQueue = q,- uvarRun = runQueue q,- uvarXS = xs,- uvarYS = ys, - uvarChangedSource = s, - uvarUpdatedSource = u }---- | Read the value of a variable, forcing the bound event queue to raise --- the events in case of need.-readUVar :: (MArray IOUArray a IO) => UVar a -> Dynamics a-readUVar v =- Dynamics $ \p ->- do invokeDynamics p $ uvarRun v- let xs = uvarXS v- ys = uvarYS v- t = pointTime p- count <- UV.vectorCount xs- let i = count - 1- x <- UV.readVector xs i- if x <= t - then UV.readVector ys i- else do i <- UV.vectorBinarySearch xs t- if i >= 0- then UV.readVector ys i- else UV.readVector ys $ - (i + 1) - 1---- | Write a new value into the variable.-writeUVar :: (MArray IOUArray a IO) => UVar a -> a -> Dynamics ()-writeUVar v a =- Dynamics $ \p ->- do let xs = uvarXS v- ys = uvarYS v- t = pointTime p- s = uvarChangedSource v- count <- UV.vectorCount xs- let i = count - 1- x <- UV.readVector xs i- if t < x - then error "Cannot update the past data: writeUVar."- else if t == x- then UV.writeVector ys i $! a- else do UV.appendVector xs t- UV.appendVector ys $! a- invokeDynamics p $ triggerSignal s a---- | Mutate the contents of the variable, forcing the bound event queue to--- raise all pending events in case of need.-modifyUVar :: (MArray IOUArray a IO) => UVar a -> (a -> a) -> Dynamics ()-modifyUVar v f =- Dynamics $ \p ->- do invokeDynamics p $ uvarRun v- let xs = uvarXS v- ys = uvarYS v- t = pointTime p- s = uvarChangedSource v- count <- UV.vectorCount xs- let i = count - 1- x <- UV.readVector xs i- if t < x- then error "Cannot update the past data: modifyUVar."- else if t == x- then do a <- UV.readVector ys i- let b = f a- UV.writeVector ys i $! b- invokeDynamics p $ triggerSignal s b- else do i <- UV.vectorBinarySearch xs t- if i >= 0- then do a <- UV.readVector ys i- let b = f a- UV.appendVector xs t- UV.appendVector ys $! b- invokeDynamics p $ triggerSignal s b- else do a <- UV.readVector ys $ - (i + 1) - 1- let b = f a- UV.appendVector xs t- UV.appendVector ys $! b- invokeDynamics p $ triggerSignal s b---- | Freeze the variable and return in arrays the time points and corresponded --- values when the variable had changed.-freezeUVar :: (MArray IOUArray a IO) => - UVar a -> Dynamics (Array Int Double, Array Int a)-freezeUVar v =- Dynamics $ \p ->- do invokeDynamics p $ uvarRun v- xs <- UV.freezeVector (uvarXS v)- ys <- UV.freezeVector (uvarYS v)- return (xs, ys)- --- | Return a signal that notifies about every change of the variable state.-uvarChanged :: UVar a -> Signal a-uvarChanged v = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (uvarUpdatedSource v)- m2 = publishSignal (uvarChangedSource v)---- | Return a signal that notifies about every change of the variable state.-uvarChanged_ :: UVar a -> Signal ()-uvarChanged_ v = mapSignal (const ()) $ uvarChanged v --invokeDynamics :: Point -> Dynamics a -> IO a-{-# INLINE invokeDynamics #-}-invokeDynamics p (Dynamics m) = m p--invokeSimulation :: Run -> Simulation a -> IO a-{-# INLINE invokeSimulation #-}-invokeSimulation r (Simulation m) = m r
− Simulation/Aivika/Dynamics/Var.hs
@@ -1,163 +0,0 @@---- |--- Module : Simulation.Aivika.Dynamics.Var--- Copyright : Copyright (c) 2009-2013, 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 a variable that is bound to the event queue and --- that keeps the history of changes storing the values in an array.----module Simulation.Aivika.Dynamics.Var- (Var,- varQueue,- varChanged,- varChanged_,- newVar,- readVar,- writeVar,- modifyVar,- freezeVar) where--import Data.Array-import Data.Array.IO.Safe-import Data.IORef--import Simulation.Aivika.Dynamics.Internal.Simulation-import Simulation.Aivika.Dynamics.Internal.Dynamics-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Internal.Signal-import Simulation.Aivika.Dynamics.Signal--import qualified Simulation.Aivika.Vector as V-import qualified Simulation.Aivika.UVector as UV---- | Like the 'Ref' reference but keeps the history of changes in --- different time points. The 'Var' variable is safe in the hybrid --- simulation and when you use different event queues, but this variable is --- slower than references.-data Var a = - Var { varQueue :: EventQueue, -- ^ Return the bound event queue.- varRun :: Dynamics (),- varXS :: UV.UVector Double, - varYS :: V.Vector a,- varChangedSource :: SignalSource a, - varUpdatedSource :: SignalSource a }- --- | Create a new variable bound to the specified event queue.-newVar :: EventQueue -> a -> Simulation (Var a)-newVar q a =- Simulation $ \r ->- do xs <- UV.newVector- ys <- V.newVector- UV.appendVector xs $ spcStartTime $ runSpecs r- V.appendVector ys a- s <- invokeSimulation r newSignalSourceUnsafe- u <- invokeSimulation r $ newSignalSource q- return Var { varQueue = q,- varRun = runQueue q,- varXS = xs,- varYS = ys, - varChangedSource = s, - varUpdatedSource = u }---- | Read the value of a variable, forcing the bound event queue to raise --- the events in case of need.-readVar :: Var a -> Dynamics a-readVar v =- Dynamics $ \p ->- do invokeDynamics p $ varRun v- let xs = varXS v- ys = varYS v- t = pointTime p- count <- UV.vectorCount xs- let i = count - 1- x <- UV.readVector xs i- if x <= t - then V.readVector ys i- else do i <- UV.vectorBinarySearch xs t- if i >= 0- then V.readVector ys i- else V.readVector ys $ - (i + 1) - 1---- | Write a new value into the variable.-writeVar :: Var a -> a -> Dynamics ()-writeVar v a =- Dynamics $ \p ->- do let xs = varXS v- ys = varYS v- t = pointTime p- s = varChangedSource v- count <- UV.vectorCount xs- let i = count - 1- x <- UV.readVector xs i- if t < x - then error "Cannot update the past data: writeVar."- else if t == x- then V.writeVector ys i $! a- else do UV.appendVector xs t- V.appendVector ys $! a- invokeDynamics p $ triggerSignal s a---- | Mutate the contents of the variable, forcing the bound event queue to--- raise all pending events in case of need.-modifyVar :: Var a -> (a -> a) -> Dynamics ()-modifyVar v f =- Dynamics $ \p ->- do invokeDynamics p $ varRun v- let xs = varXS v- ys = varYS v- t = pointTime p- s = varChangedSource v- count <- UV.vectorCount xs- let i = count - 1- x <- UV.readVector xs i- if t < x- then error "Cannot update the past data: modifyVar."- else if t == x- then do a <- V.readVector ys i- let b = f a- V.writeVector ys i $! b- invokeDynamics p $ triggerSignal s b- else do i <- UV.vectorBinarySearch xs t- if i >= 0- then do a <- V.readVector ys i- let b = f a- UV.appendVector xs t- V.appendVector ys $! b- invokeDynamics p $ triggerSignal s b- else do a <- V.readVector ys $ - (i + 1) - 1- let b = f a- UV.appendVector xs t- V.appendVector ys $! b- invokeDynamics p $ triggerSignal s b---- | Freeze the variable and return in arrays the time points and corresponded --- values when the variable had changed.-freezeVar :: Var a -> Dynamics (Array Int Double, Array Int a)-freezeVar v =- Dynamics $ \p ->- do invokeDynamics p $ varRun v- xs <- UV.freezeVector (varXS v)- ys <- V.freezeVector (varYS v)- return (xs, ys)- --- | Return a signal that notifies about every change of the variable state.-varChanged :: Var a -> Signal a-varChanged v = merge2Signals m1 m2 -- N.B. The order is important!- where m1 = publishSignal (varUpdatedSource v)- m2 = publishSignal (varChangedSource v)---- | Return a signal that notifies about every change of the variable state.-varChanged_ :: Var a -> Signal ()-varChanged_ v = mapSignal (const ()) $ varChanged v --invokeDynamics :: Point -> Dynamics a -> IO a-{-# INLINE invokeDynamics #-}-invokeDynamics p (Dynamics m) = m p--invokeSimulation :: Run -> Simulation a -> IO a-{-# INLINE invokeSimulation #-}-invokeSimulation r (Simulation m) = m r
+ Simulation/Aivika/Event.hs view
@@ -0,0 +1,36 @@++-- |+-- Module : Simulation.Aivika.Event+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The module defines the 'Event' monad which is very similar to the 'Dynamics'+-- monad but only now the computation is strongly synchronized with the event queue.+--+module Simulation.Aivika.Event+ (-- * Event Monad+ Event,+ EventLift(..),+ EventProcessing(..),+ EventCancellation(..),+ runEvent,+ runEventInStartTime,+ runEventInStopTime,+ -- * Event Queue+ enqueueEvent,+ enqueueEventWithCancellation,+ enqueueEventWithTimes,+ enqueueEventWithIntegTimes,+ enqueueEventWithStartTime,+ enqueueEventWithStopTime,+ enqueueEventWithCurrentTime,+ eventQueueCount,+ -- * Error Handling+ catchEvent,+ finallyEvent,+ throwEvent) where++import Simulation.Aivika.Internal.Event
+ Simulation/Aivika/Internal/Cont.hs view
@@ -0,0 +1,320 @@++-- |+-- Module : Simulation.Aivika.Internal.Cont+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The 'Cont' monad is a variation of the standard Cont monad +-- and F# async workflow, where the result of applying +-- the continuations is the 'Event' computation.+--+module Simulation.Aivika.Internal.Cont+ (Cont(..),+ ContParams,+ invokeCont,+ runCont,+ catchCont,+ finallyCont,+ throwCont,+ resumeCont,+ contCanceled) where++import Data.IORef++import qualified Control.Exception as C+import Control.Exception (IOException, throw)++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics+import Simulation.Aivika.Internal.Event++-- | The 'Cont' type is similar to the standard Cont monad +-- and F# async workflow but only the result of applying+-- the continuations return the 'Event' computation.+newtype Cont a = Cont (ContParams a -> Event ())++-- | The continuation parameters.+data ContParams a = + ContParams { contCont :: a -> Event (), + contAux :: ContParamsAux }++-- | The auxiliary continuation parameters.+data ContParamsAux =+ ContParamsAux { contECont :: IOException -> Event (),+ contCCont :: () -> Event (),+ contCancelToken :: IORef Bool,+ contCatchFlag :: Bool }++instance Monad Cont where+ return = returnC+ m >>= k = bindC m k++instance SimulationLift Cont where+ liftSimulation = liftSC++instance DynamicsLift Cont where+ liftDynamics = liftDC++instance EventLift Cont where+ liftEvent = liftEC++instance Functor Cont where+ fmap = liftM++instance MonadIO Cont where+ liftIO = liftIOC ++invokeCont :: ContParams a -> Cont a -> Event ()+{-# INLINE invokeCont #-}+invokeCont p (Cont m) = m p++cancelCont :: Point -> ContParams a -> IO ()+{-# NOINLINE cancelCont #-}+cancelCont p c =+ do writeIORef (contCancelToken $ contAux c) False+ invokeEvent p $ (contCCont $ contAux c) ()++returnC :: a -> Cont a+{-# INLINE returnC #-}+returnC a = + Cont $ \c ->+ Event $ \p ->+ do z <- contCanceled c+ if z + then cancelCont p c+ else invokeEvent p $ contCont c a+ +-- bindC :: Cont a -> (a -> Cont b) -> Cont b+-- {-# INLINE bindC #-}+-- bindC m k = +-- Cont $ \c -> +-- if (contCatchFlag . contAux $ c) +-- then bindWithCatch m k c+-- else bindWithoutCatch m k c+ +bindC :: Cont a -> (a -> Cont b) -> Cont b+{-# INLINE bindC #-}+bindC m k = + Cont $ bindWithoutCatch m k -- Another version is not tail recursive!+ +bindWithoutCatch :: Cont a -> (a -> Cont b) -> ContParams b -> Event ()+{-# INLINE bindWithoutCatch #-}+bindWithoutCatch (Cont m) k c = + Event $ \p ->+ do z <- contCanceled c+ if z + then cancelCont p c+ else invokeEvent p $ m $ + let cont a = invokeCont c (k a)+ in c { contCont = cont }++-- It is not tail recursive!+bindWithCatch :: Cont a -> (a -> Cont b) -> ContParams b -> Event ()+{-# NOINLINE bindWithCatch #-}+bindWithCatch (Cont m) k c = + Event $ \p ->+ do z <- contCanceled c+ if z + then cancelCont p c+ else invokeEvent p $ m $ + let cont a = catchEvent + (invokeCont c (k a))+ (contECont $ contAux c)+ in c { contCont = cont }++-- Like "bindWithoutCatch (return a) k"+callWithoutCatch :: (a -> Cont b) -> a -> ContParams b -> Event ()+callWithoutCatch k a c =+ Event $ \p ->+ do z <- contCanceled c+ if z + then cancelCont p c+ else invokeEvent p $ invokeCont c (k a)++-- Like "bindWithCatch (return a) k" but it is not tail recursive!+callWithCatch :: (a -> Cont b) -> a -> ContParams b -> Event ()+callWithCatch k a c =+ Event $ \p ->+ do z <- contCanceled c+ if z + then cancelCont p c+ else invokeEvent p $ catchEvent + (invokeCont c (k a))+ (contECont $ contAux c)++-- | Exception handling within 'Cont' computations.+catchCont :: Cont a -> (IOException -> Cont a) -> Cont a+catchCont m h = + Cont $ \c -> + if contCatchFlag . contAux $ c+ then catchWithCatch m h c+ else error $+ "To catch exceptions, the process must be created " +++ "with help of newProcessIDWithCatch: catchCont."+ +catchWithCatch :: Cont a -> (IOException -> Cont a) -> ContParams a -> Event ()+catchWithCatch (Cont m) h c =+ Event $ \p -> + do z <- contCanceled c+ if z + then cancelCont p c+ else invokeEvent p $ m $+ -- let econt e = callWithCatch h e c -- not tail recursive!+ let econt e = callWithoutCatch h e c+ in c { contAux = (contAux c) { contECont = econt } }+ +-- | A computation with finalization part.+finallyCont :: Cont a -> Cont b -> Cont a+finallyCont m m' = + Cont $ \c -> + if contCatchFlag . contAux $ c+ then finallyWithCatch m m' c+ else error $+ "To finalize computation, the process must be created " +++ "with help of newProcessIdWithCatch: finallyCont."+ +finallyWithCatch :: Cont a -> Cont b -> ContParams a -> Event () +finallyWithCatch (Cont m) (Cont m') c =+ Event $ \p ->+ do z <- contCanceled c+ if z + then cancelCont p c+ else invokeEvent p $ m $+ let cont a = + Event $ \p ->+ invokeEvent p $ m' $+ let cont b = contCont c a+ in c { contCont = cont }+ econt e =+ Event $ \p ->+ invokeEvent p $ m' $+ let cont b = (contECont . contAux $ c) e+ in c { contCont = cont }+ ccont () = + Event $ \p ->+ invokeEvent p $ m' $+ let cont b = (contCCont . contAux $ c) ()+ econt e = (contCCont . contAux $ c) ()+ in c { contCont = cont,+ contAux = (contAux c) { contECont = econt } }+ in c { contCont = cont,+ contAux = (contAux c) { contECont = econt,+ contCCont = ccont } }++-- | Throw the exception with the further exception handling.+-- By some reasons, the standard 'throw' function per se is not handled +-- properly within 'Cont' computations, altough it will be still handled +-- if it will be hidden under the 'liftIO' function. The problem arises +-- namely with the @throw@ function, not 'IO' computations.+throwCont :: IOException -> Cont a+throwCont e = liftIO $ throw e++-- | Run the 'Cont' computation with the specified cancelation token +-- and flag indicating whether to catch exceptions.+runCont :: Cont a+ -- ^ the computation to run+ -> (a -> Event ())+ -- ^ the main branch + -> (IOError -> Event ())+ -- ^ the branch for handing exceptions+ -> (() -> Event ())+ -- ^ the branch for cancellation+ -> IORef Bool+ -- ^ the cancellation token+ -> Bool+ -- ^ whether to support the exception catching+ -> Event ()+runCont (Cont m) cont econt ccont cancelToken catchFlag = + m ContParams { contCont = cont,+ contAux = + ContParamsAux { contECont = econt,+ contCCont = ccont,+ contCancelToken = cancelToken, + contCatchFlag = catchFlag } }++-- | Lift the 'Simulation' computation.+liftSC :: Simulation a -> Cont a+liftSC (Simulation m) = + Cont $ \c ->+ Event $ \p ->+ if contCatchFlag . contAux $ c+ then liftIOWithCatch (m $ pointRun p) p c+ else liftIOWithoutCatch (m $ pointRun p) p c+ +-- | Lift the 'Dynamics' computation.+liftDC :: Dynamics a -> Cont a+liftDC (Dynamics m) =+ Cont $ \c ->+ Event $ \p ->+ if contCatchFlag . contAux $ c+ then liftIOWithCatch (m p) p c+ else liftIOWithoutCatch (m p) p c+ +-- | Lift the 'Event' computation.+liftEC :: Event a -> Cont a+liftEC (Event m) =+ Cont $ \c ->+ Event $ \p ->+ if contCatchFlag . contAux $ c+ then liftIOWithCatch (m p) p c+ else liftIOWithoutCatch (m p) p c+ +-- | Lift the IO computation.+liftIOC :: IO a -> Cont a+liftIOC m =+ Cont $ \c ->+ Event $ \p ->+ if contCatchFlag . contAux $ c+ then liftIOWithCatch m p c+ else liftIOWithoutCatch m p c+ +liftIOWithoutCatch :: IO a -> Point -> ContParams a -> IO ()+{-# INLINE liftIOWithoutCatch #-}+liftIOWithoutCatch m p c =+ do z <- contCanceled c+ if z+ then cancelCont p c+ else do a <- m+ invokeEvent p $ contCont c a++liftIOWithCatch :: IO a -> Point -> ContParams a -> IO ()+{-# NOINLINE liftIOWithCatch #-}+liftIOWithCatch m p c =+ do z <- contCanceled c+ if z+ then cancelCont p c+ else do aref <- newIORef undefined+ eref <- newIORef Nothing+ C.catch (m >>= writeIORef aref) + (writeIORef eref . Just)+ e <- readIORef eref+ case e of+ Nothing -> + do a <- readIORef aref+ -- tail recursive+ invokeEvent p $ contCont c a+ Just e ->+ -- tail recursive+ invokeEvent p $ (contECont . contAux) c e++-- | Resume the computation by the specified parameters.+resumeCont :: ContParams a -> a -> Event ()+{-# INLINE resumeCont #-}+resumeCont c a = + Event $ \p ->+ do z <- contCanceled c+ if z+ then cancelCont p c+ else invokeEvent p $ contCont c a++-- | Test whether the computation is canceled.+contCanceled :: ContParams a -> IO Bool+{-# INLINE contCanceled #-}+contCanceled c = readIORef $ contCancelToken $ contAux c
+ Simulation/Aivika/Internal/Dynamics.hs view
@@ -0,0 +1,217 @@++{-# LANGUAGE RecursiveDo #-}++-- |+-- Module : Simulation.Aivika.Internal.Dynamics+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The module defines the 'Dynamics' monad representing a time varying polymorphic function. +--+module Simulation.Aivika.Internal.Dynamics+ (-- * Dynamics+ Dynamics(..),+ DynamicsLift(..),+ invokeDynamics,+ runDynamicsInStartTime,+ runDynamicsInStopTime,+ runDynamicsInIntegTimes,+ runDynamicsInTime,+ runDynamicsInTimes,+ -- * Error Handling+ catchDynamics,+ finallyDynamics,+ throwDynamics,+ -- * Time parameters+ starttime,+ stoptime,+ dt,+ time,+ isTimeInteg,+ integIteration,+ integPhase) where++import qualified Control.Exception as C+import Control.Exception (IOException, throw, finally)++import Control.Monad+import Control.Monad.Trans+import Control.Monad.Fix++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation++-- | A value in the 'Dynamics' monad represents a polymorphic time varying function.+newtype Dynamics a = Dynamics (Point -> IO a)++instance Monad Dynamics where+ return = returnD+ m >>= k = bindD m k++returnD :: a -> Dynamics a+{-# INLINE returnD #-}+returnD a = Dynamics (\p -> return a)++bindD :: Dynamics a -> (a -> Dynamics b) -> Dynamics b+{-# INLINE bindD #-}+bindD (Dynamics m) k = + Dynamics $ \p -> + do a <- m p+ let Dynamics m' = k a+ m' p++-- | Run the 'Dynamics' computation in the initial time point.+runDynamicsInStartTime :: Dynamics a -> Simulation a+runDynamicsInStartTime (Dynamics m) =+ Simulation $ m . integStartPoint++-- | Run the 'Dynamics' computation in the final time point.+runDynamicsInStopTime :: Dynamics a -> Simulation a+runDynamicsInStopTime (Dynamics m) =+ Simulation $ m . integStopPoint++-- | Run the 'Dynamics' computation in all integration time points.+runDynamicsInIntegTimes :: Dynamics a -> Simulation [IO a]+runDynamicsInIntegTimes (Dynamics m) =+ Simulation $ return . map m . integPoints++-- | Run the 'Dynamics' computation in the specified time point.+runDynamicsInTime :: Double -> Dynamics a -> Simulation a+runDynamicsInTime t (Dynamics m) =+ Simulation $ \r -> m $ pointAt r t++-- | Run the 'Dynamics' computation in the specified time points.+runDynamicsInTimes :: [Double] -> Dynamics a -> Simulation [IO a]+runDynamicsInTimes ts (Dynamics m) =+ Simulation $ \r -> return $ map (m . pointAt r) ts ++instance Functor Dynamics where+ fmap = liftMD++instance Eq (Dynamics a) where+ x == y = error "Can't compare dynamics." ++instance Show (Dynamics a) where+ showsPrec _ x = showString "<< Dynamics >>"++liftMD :: (a -> b) -> Dynamics a -> Dynamics b+{-# INLINE liftMD #-}+liftMD f (Dynamics x) =+ Dynamics $ \p -> do { a <- x p; return $ f a }++liftM2D :: (a -> b -> c) -> Dynamics a -> Dynamics b -> Dynamics c+{-# INLINE liftM2D #-}+liftM2D f (Dynamics x) (Dynamics y) =+ Dynamics $ \p -> do { a <- x p; b <- y p; return $ f a b }++instance (Num a) => Num (Dynamics a) where+ x + y = liftM2D (+) x y+ x - y = liftM2D (-) x y+ x * y = liftM2D (*) x y+ negate = liftMD negate+ abs = liftMD abs+ signum = liftMD signum+ fromInteger i = return $ fromInteger i++instance (Fractional a) => Fractional (Dynamics a) where+ x / y = liftM2D (/) x y+ recip = liftMD recip+ fromRational t = return $ fromRational t++instance (Floating a) => Floating (Dynamics a) where+ pi = return pi+ exp = liftMD exp+ log = liftMD log+ sqrt = liftMD sqrt+ x ** y = liftM2D (**) x y+ sin = liftMD sin+ cos = liftMD cos+ tan = liftMD tan+ asin = liftMD asin+ acos = liftMD acos+ atan = liftMD atan+ sinh = liftMD sinh+ cosh = liftMD cosh+ tanh = liftMD tanh+ asinh = liftMD asinh+ acosh = liftMD acosh+ atanh = liftMD atanh++instance MonadIO Dynamics where+ liftIO m = Dynamics $ const m++instance SimulationLift Dynamics where+ liftSimulation = liftDS+ +liftDS :: Simulation a -> Dynamics a+{-# INLINE liftDS #-}+liftDS (Simulation m) =+ Dynamics $ \p -> m $ pointRun p++-- | A type class to lift the 'Dynamics' computations to other monads.+class Monad m => DynamicsLift m where+ + -- | Lift the specified 'Dynamics' computation to another monad.+ liftDynamics :: Dynamics a -> m a++instance DynamicsLift Dynamics where+ liftDynamics = id+ +-- | Exception handling within 'Dynamics' computations.+catchDynamics :: Dynamics a -> (IOException -> Dynamics a) -> Dynamics a+catchDynamics (Dynamics m) h =+ Dynamics $ \p -> + C.catch (m p) $ \e ->+ let Dynamics m' = h e in m' p+ +-- | A computation with finalization part like the 'finally' function.+finallyDynamics :: Dynamics a -> Dynamics b -> Dynamics a+finallyDynamics (Dynamics m) (Dynamics m') =+ Dynamics $ \p ->+ C.finally (m p) (m' p)++-- | Like the standard 'throw' function.+throwDynamics :: IOException -> Dynamics a+throwDynamics = throw++-- | Invoke the 'Dynamics' computation.+invokeDynamics :: Point -> Dynamics a -> IO a+{-# INLINE invokeDynamics #-}+invokeDynamics p (Dynamics m) = m p++instance MonadFix Dynamics where+ mfix f = + Dynamics $ \p ->+ do { rec { a <- invokeDynamics p (f a) }; return a }++-- | Return the start simulation time.+starttime :: Dynamics Double+starttime = Dynamics $ return . spcStartTime . pointSpecs++-- | Return the stop simulation time.+stoptime :: Dynamics Double+stoptime = Dynamics $ return . spcStopTime . pointSpecs++-- | Return the integration time step.+dt :: Dynamics Double+dt = Dynamics $ return . spcDT . pointSpecs++-- | Return the current simulation time.+time :: Dynamics Double+time = Dynamics $ return . pointTime ++-- | Whether the current time is an integration time.+isTimeInteg :: Dynamics Bool+isTimeInteg = Dynamics $ \p -> return $ pointPhase p >= 0++-- | Return the integration iteration closest to the current simulation time.+integIteration :: Dynamics Int+integIteration = Dynamics $ return . pointIteration++-- | Return the integration phase for the current simulation time.+-- It is @(-1)@ for non-integration time points.+integPhase :: Dynamics Int+integPhase = Dynamics $ return . pointPhase
+ Simulation/Aivika/Internal/Event.hs view
@@ -0,0 +1,349 @@++{-# LANGUAGE RecursiveDo #-}++-- |+-- Module : Simulation.Aivika.Internal.Event+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The module defines the 'Event' monad which is very similar to the 'Dynamics'+-- monad but only now the computation is strongly synchronized with the event queue.+--+module Simulation.Aivika.Internal.Event+ (-- * Event Monad+ Event(..),+ EventLift(..),+ EventProcessing(..),+ EventCancellation(..),+ invokeEvent,+ runEvent,+ runEventInStartTime,+ runEventInStopTime,+ -- * Event Queue+ enqueueEvent,+ enqueueEventWithCancellation,+ enqueueEventWithTimes,+ enqueueEventWithPoints,+ enqueueEventWithIntegTimes,+ enqueueEventWithStartTime,+ enqueueEventWithStopTime,+ enqueueEventWithCurrentTime,+ eventQueueCount,+ -- * Error Handling+ catchEvent,+ finallyEvent,+ throwEvent) where++import Data.IORef++import qualified Control.Exception as C+import Control.Exception (IOException, throw, finally)++import Control.Monad+import Control.Monad.Trans+import Control.Monad.Fix++import qualified Simulation.Aivika.PriorityQueue as PQ++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics++-- | A value in the 'Event' monad represents a polymorphic time varying function+-- which is strongly synchronized with the event queue.+newtype Event a = Event (Point -> IO a)++instance Monad Event where+ return = returnE+ m >>= k = bindE m k++returnE :: a -> Event a+{-# INLINE returnE #-}+returnE a = Event (\p -> return a)++bindE :: Event a -> (a -> Event b) -> Event b+{-# INLINE bindE #-}+bindE (Event m) k = + Event $ \p -> + do a <- m p+ let Event m' = k a+ m' p++instance Functor Event where+ fmap = liftME++liftME :: (a -> b) -> Event a -> Event b+{-# INLINE liftME #-}+liftME f (Event x) =+ Event $ \p -> do { a <- x p; return $ f a }++instance MonadIO Event where+ liftIO m = Event $ const m++instance SimulationLift Event where+ liftSimulation = liftES++instance DynamicsLift Event where+ liftDynamics = liftDS+ +liftES :: Simulation a -> Event a+{-# INLINE liftES #-}+liftES (Simulation m) =+ Event $ \p -> m $ pointRun p++liftDS :: Dynamics a -> Event a+{-# INLINE liftDS #-}+liftDS (Dynamics m) =+ Event m++-- | A type class to lift the 'Event' computation to other monads.+class Monad m => EventLift m where+ + -- | Lift the specified 'Event' computation to another monad.+ liftEvent :: Event a -> m a++instance EventLift Event where+ liftEvent = id+ +-- | Exception handling within 'Event' computations.+catchEvent :: Event a -> (IOException -> Event a) -> Event a+catchEvent (Event m) h =+ Event $ \p -> + C.catch (m p) $ \e ->+ let Event m' = h e in m' p+ +-- | A computation with finalization part like the 'finally' function.+finallyEvent :: Event a -> Event b -> Event a+finallyEvent (Event m) (Event m') =+ Event $ \p ->+ C.finally (m p) (m' p)++-- | Like the standard 'throw' function.+throwEvent :: IOException -> Event a+throwEvent = throw++-- | Invoke the 'Event' computation.+invokeEvent :: Point -> Event a -> IO a+{-# INLINE invokeEvent #-}+invokeEvent p (Event m) = m p++instance MonadFix Event where+ mfix f = + Event $ \p ->+ do { rec { a <- invokeEvent p (f a) }; return a }++-- | Defines how the events are processed.+data EventProcessing = IncludingCurrentEvents+ -- ^ 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+ -- ^ 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+ -- ^ 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+ -- ^ either process all earlier 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)+ deriving (Eq, Ord, Show)++-- | Enqueue the event which must be actuated at the specified time.+--+-- The events are processed when calling the 'runEvent' function. So,+-- if you want to insist on their immediate execution then you can apply+-- something like+--+-- @+-- liftDynamics $ runEvent IncludingCurrentEvents $ return ()+-- @+--+-- although this is generally not good idea. +enqueueEvent :: Double -> Event () -> Event ()+enqueueEvent t (Event m) =+ Event $ \p ->+ let pq = queuePQ $ runEventQueue $ pointRun p+ in PQ.enqueue pq t m++-- | Process the pending events.+processPendingEventsCore :: Bool -> Dynamics ()+processPendingEventsCore includingCurrentEvents = Dynamics r where+ r p =+ do let q = runEventQueue $ pointRun p+ f = queueBusy q+ f' <- readIORef f+ unless f' $+ do writeIORef f True+ call q p+ writeIORef f False+ call q p =+ do let pq = queuePQ q+ r = pointRun p+ f <- PQ.queueNull pq+ unless f $+ do (t2, c2) <- PQ.queueFront pq+ let t = queueTime q+ t' <- readIORef t+ when (t2 < t') $ + error "The time value is too small: processPendingEventsCore"+ when ((t2 < pointTime p) ||+ (includingCurrentEvents && (t2 == pointTime p))) $+ do writeIORef t t2+ PQ.dequeue pq+ let sc = pointSpecs p+ t0 = spcStartTime sc+ dt = spcDT sc+ n2 = fromIntegral $ floor ((t2 - t0) / dt)+ c2 $ p { pointTime = t2,+ pointIteration = n2,+ pointPhase = -1 }+ call q p++-- | Process the pending events synchronously, i.e. without past.+processPendingEvents :: Bool -> Dynamics ()+processPendingEvents includingCurrentEvents = Dynamics r where+ r p =+ do let q = runEventQueue $ pointRun p+ t = queueTime q+ t' <- readIORef t+ if pointTime p < t'+ then error $+ "The current time is less than " +++ "the time in the queue: processPendingEvents"+ else invokeDynamics p m+ m = processPendingEventsCore includingCurrentEvents++-- | A memoized value.+processEventsIncludingCurrent = processPendingEvents True++-- | A memoized value.+processEventsIncludingEarlier = processPendingEvents False++-- | A memoized value.+processEventsIncludingCurrentCore = processPendingEventsCore True++-- | A memoized value.+processEventsIncludingEarlierCore = processPendingEventsCore True++-- | Process the events.+processEvents :: EventProcessing -> Dynamics ()+processEvents IncludingCurrentEvents = processEventsIncludingCurrent+processEvents IncludingEarlierEvents = processEventsIncludingEarlier+processEvents IncludingCurrentEventsOrFromPast = processEventsIncludingCurrentCore+processEvents IncludingEarlierEventsOrFromPast = processEventsIncludingEarlierCore++-- | Run the 'Event' computation in the current simulation time+-- within the 'Dynamics' computation.+runEvent :: EventProcessing -> Event a -> Dynamics a+runEvent 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 stop time.+runEventInStopTime :: EventProcessing -> Event a -> Simulation a+runEventInStopTime processing e =+ runDynamicsInStopTime $ runEvent processing e++-- | Return the number of pending events that should+-- be yet actuated.+eventQueueCount :: Event Int+eventQueueCount =+ Event $ PQ.queueCount . queuePQ . runEventQueue . pointRun++-- | Actuate the event handler in the specified time points.+enqueueEventWithTimes :: [Double] -> Event () -> Event ()+enqueueEventWithTimes ts e = loop ts+ where loop [] = return ()+ loop (t : ts) = enqueueEvent t $ e >> loop ts+ +-- | Actuate the event handler in the specified time points.+enqueueEventWithPoints :: [Point] -> Event () -> Event ()+enqueueEventWithPoints xs (Event e) = loop xs+ where loop [] = return ()+ loop (x : xs) = enqueueEvent (pointTime x) $ + Event $ \p ->+ do e x -- N.B. we substitute the time point!+ invokeEvent p $ loop xs+ +-- | Actuate the event handler in the integration time points.+enqueueEventWithIntegTimes :: Event () -> Event ()+enqueueEventWithIntegTimes e =+ Event $ \p ->+ let points = integPoints $ pointRun p+ in invokeEvent p $ enqueueEventWithPoints points e++-- | Actuate the event handler in the start time.+enqueueEventWithStartTime :: Event () -> Event ()+enqueueEventWithStartTime e =+ Event $ \p ->+ let point = integStartPoint $ pointRun p+ in invokeEvent p $ enqueueEventWithPoints [point] e++-- | Actuate the event handler in the stop time.+enqueueEventWithStopTime :: Event () -> Event ()+enqueueEventWithStopTime e =+ Event $ \p ->+ let point = integStopPoint $ pointRun p+ in invokeEvent p $ enqueueEventWithPoints [point] e++-- | Actuate the event handler in the current time but +-- through the event queue, which allows continuing the +-- current tasks and then calling the handler after the +-- tasks are finished. The simulation time will be the same.+enqueueEventWithCurrentTime :: Event () -> Event ()+enqueueEventWithCurrentTime e =+ Event $ \p ->+ invokeEvent p $ enqueueEvent (pointTime p) e++-- | It allows cancelling the event.+data EventCancellation =+ EventCancellation { cancelEvent :: Event (),+ -- ^ Cancel the event.+ eventCanceled :: Event Bool,+ -- ^ Test whether the event was canceled.+ eventFinished :: Event Bool+ -- ^ Test whether the event was processed and finished.+ }++-- | Enqueue the event with an ability to cancel it.+enqueueEventWithCancellation :: Double -> Event () -> Event EventCancellation+enqueueEventWithCancellation t e =+ Event $ \p ->+ do canceledRef <- newIORef False+ cancellableRef <- newIORef True+ finishedRef <- newIORef False+ let cancel =+ Event $ \p ->+ do x <- readIORef cancellableRef+ when x $+ writeIORef canceledRef True+ canceled =+ Event $ \p -> readIORef canceledRef+ finished =+ Event $ \p -> readIORef finishedRef+ invokeEvent p $+ enqueueEvent t $+ Event $ \p ->+ do writeIORef cancellableRef False+ x <- readIORef canceledRef+ unless x $+ do invokeEvent p e+ writeIORef finishedRef True+ return EventCancellation { cancelEvent = cancel,+ eventCanceled = canceled,+ eventFinished = finished }
+ Simulation/Aivika/Internal/Process.hs view
@@ -0,0 +1,325 @@++-- |+-- Module : Simulation.Aivika.Internal.Process+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- A value in the 'Process' monad represents a discontinuous process that +-- can suspend in any simulation time point and then resume later in the same +-- or another time point. +-- +-- The process of this type can involve the 'Event', 'Dynamics' and 'Simulation'+-- computations. Moreover, a value in the @Process@ monad can be run within+-- the @Event@ computation.+--+-- A value of the 'ProcessId' type is just an identifier of such a process.+--+module Simulation.Aivika.Internal.Process+ (ProcessId,+ Process(..),+ invokeProcess,+ runProcess,+ runProcessInStartTime,+ runProcessInStopTime,+ enqueueProcess,+ enqueueProcessWithStartTime,+ enqueueProcessWithStopTime,+ newProcessId,+ newProcessIdWithCatch,+ holdProcess,+ interruptProcess,+ processInterrupted,+ passivateProcess,+ processPassive,+ reactivateProcess,+ processId,+ cancelProcess,+ processCanceled,+ catchProcess,+ finallyProcess,+ throwProcess) where++import Data.Maybe+import Data.IORef+import Control.Exception (IOException, throw)+import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Cont++-- | Represents a process identifier.+data ProcessId = + ProcessId { processStarted :: IORef Bool,+ processCatchFlag :: Bool,+ processReactCont :: IORef (Maybe (ContParams ())), + processCancelRef :: IORef Bool, + processCancelToken :: IORef Bool,+ processInterruptRef :: IORef Bool, + processInterruptCont :: IORef (Maybe (ContParams ())), + processInterruptVersion :: IORef Int }++-- | Specifies a discontinuous process that can suspend at any time+-- and then resume later.+newtype Process a = Process (ProcessId -> Cont a)++-- | Invoke the process computation.+invokeProcess :: ProcessId -> Process a -> Cont a+{-# INLINE invokeProcess #-}+invokeProcess pid (Process m) = m pid++-- | Hold the process for the specified time period.+holdProcess :: Double -> Process ()+holdProcess dt =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let x = processInterruptCont pid+ writeIORef x $ Just c+ writeIORef (processInterruptRef pid) False+ v <- readIORef (processInterruptVersion pid)+ invokeEvent p $+ enqueueEvent (pointTime p + dt) $+ Event $ \p ->+ do v' <- readIORef (processInterruptVersion pid)+ when (v == v') $ + do writeIORef x Nothing+ invokeEvent p $ resumeCont c ()++-- | Interrupt a process with the specified identifier if the process+-- is held by computation 'holdProcess'.+interruptProcess :: ProcessId -> Event ()+interruptProcess pid =+ Event $ \p ->+ do let x = processInterruptCont pid+ a <- readIORef x+ case a of+ Nothing -> return ()+ Just c ->+ do writeIORef x Nothing+ writeIORef (processInterruptRef pid) True+ modifyIORef (processInterruptVersion pid) $ (+) 1+ invokeEvent p $ enqueueEvent (pointTime p) $ resumeCont c ()+ +-- | Test whether the process with the specified identifier was interrupted.+processInterrupted :: ProcessId -> Event Bool+processInterrupted pid =+ Event $ \p ->+ readIORef (processInterruptRef pid)++-- | Passivate the process.+passivateProcess :: Process ()+passivateProcess =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let x = processReactCont pid+ a <- readIORef x+ case a of+ Nothing -> writeIORef x $ Just c+ Just _ -> error "Cannot passivate the process twice: passivate"++-- | Test whether the process with the specified identifier is passivated.+processPassive :: ProcessId -> Event Bool+processPassive pid =+ Event $ \p ->+ do let x = processReactCont pid+ a <- readIORef x+ return $ isJust a++-- | Reactivate a process with the specified identifier.+reactivateProcess :: ProcessId -> Event ()+reactivateProcess pid =+ Event $ \p ->+ do let x = processReactCont pid+ a <- readIORef x+ case a of+ Nothing -> + return ()+ Just c ->+ do writeIORef x Nothing+ invokeEvent p $ enqueueEvent (pointTime p) $ resumeCont c ()++-- | Start immediately the process with the specified identifier.+-- +-- To run the process at the specified time, you can use+-- the 'enqueueProcess' function.+runProcess :: ProcessId -> Process () -> Event ()+runProcess pid p =+ runCont m cont econt ccont (processCancelToken pid) (processCatchFlag pid)+ where cont = return+ econt = throwEvent+ ccont = return+ m = do y <- liftIO $ readIORef (processStarted pid)+ if y + then error $+ "Another process with this identifier " +++ "has been started already: runProcess"+ else liftIO $ writeIORef (processStarted pid) True+ invokeProcess pid p++-- | Start the process in the start time immediately.+runProcessInStartTime :: EventProcessing -> ProcessId -> Process () -> Simulation ()+runProcessInStartTime processing pid p =+ runEventInStartTime processing $ runProcess pid p++-- | Start the process in the stop time immediately.+runProcessInStopTime :: EventProcessing -> ProcessId -> Process () -> Simulation ()+runProcessInStopTime processing pid p =+ runEventInStopTime processing $ runProcess pid p++-- | Enqueue the process that will be then started at the specified time+-- from the event queue.+enqueueProcess :: Double -> ProcessId -> Process () -> Event ()+enqueueProcess t pid p =+ enqueueEvent t $ runProcess pid p++-- | Enqueue the process that will be then started in the start time+-- from the event queue.+enqueueProcessWithStartTime :: ProcessId -> Process () -> Event ()+enqueueProcessWithStartTime pid p =+ enqueueEventWithStartTime $ runProcess pid p++-- | Enqueue the process that will be then started in the stop time+-- from the event queue.+enqueueProcessWithStopTime :: ProcessId -> Process () -> Event ()+enqueueProcessWithStopTime pid p =+ enqueueEventWithStopTime $ runProcess pid p++-- | Return the current process identifier.+processId :: Process ProcessId+processId = Process return++-- | Create a new process identifier without exception handling.+newProcessId :: Simulation ProcessId+newProcessId =+ do x <- liftIO $ newIORef Nothing+ y <- liftIO $ newIORef False+ c <- liftIO $ newIORef False+ t <- liftIO $ newIORef False+ i <- liftIO $ newIORef False+ z <- liftIO $ newIORef Nothing+ v <- liftIO $ newIORef 0+ return ProcessId { processStarted = y,+ processCatchFlag = False,+ processReactCont = x, + processCancelRef = c, + processCancelToken = t,+ processInterruptRef = i,+ processInterruptCont = z, + processInterruptVersion = v }++-- | Create a new process identifier with capabilities of catching +-- the 'IOError' exceptions and finalizing the computation. +-- The corresponded process will be slower than that one+-- which identifier is created with help of 'newProcessId'.+newProcessIdWithCatch :: Simulation ProcessId+newProcessIdWithCatch =+ do x <- liftIO $ newIORef Nothing+ y <- liftIO $ newIORef False+ c <- liftIO $ newIORef False+ t <- liftIO $ newIORef False+ i <- liftIO $ newIORef False+ z <- liftIO $ newIORef Nothing+ v <- liftIO $ newIORef 0+ return ProcessId { processStarted = y,+ processCatchFlag = True,+ processReactCont = x, + processCancelRef = c, + processCancelToken = t,+ processInterruptRef = i,+ processInterruptCont = z, + processInterruptVersion = v }++-- | Cancel a process with the specified identifier.+cancelProcess :: ProcessId -> Event ()+cancelProcess pid =+ Event $ \p ->+ do z <- readIORef (processCancelRef pid) + unless z $+ do writeIORef (processCancelRef pid) True+ writeIORef (processCancelToken pid) True++-- | Test whether the process with the specified identifier was canceled.+processCanceled :: ProcessId -> Event Bool+processCanceled pid =+ Event $ \p ->+ readIORef (processCancelRef pid)++instance Eq ProcessId where+ x == y = processReactCont x == processReactCont y -- for the references are unique++instance Monad Process where+ return = returnP+ m >>= k = bindP m k++instance Functor Process where+ fmap = liftM++instance SimulationLift Process where+ liftSimulation = liftSP+ +instance DynamicsLift Process where+ liftDynamics = liftDP+ +instance EventLift Process where+ liftEvent = liftEP+ +instance MonadIO Process where+ liftIO = liftIOP+ +returnP :: a -> Process a+{-# INLINE returnP #-}+returnP a = Process $ \pid -> return a++bindP :: Process a -> (a -> Process b) -> Process b+{-# INLINE bindP #-}+bindP (Process m) k = + Process $ \pid -> + do a <- m pid+ let Process m' = k a+ m' pid++liftSP :: Simulation a -> Process a+{-# INLINE liftSP #-}+liftSP m = Process $ \pid -> liftSimulation m++liftDP :: Dynamics a -> Process a+{-# INLINE liftDP #-}+liftDP m = Process $ \pid -> liftDynamics m++liftEP :: Event a -> Process a+{-# INLINE liftEP #-}+liftEP m = Process $ \pid -> liftEvent m++liftIOP :: IO a -> Process a+{-# INLINE liftIOP #-}+liftIOP m = Process $ \pid -> liftIO m++-- | Exception handling within 'Process' computations.+catchProcess :: Process a -> (IOException -> Process a) -> Process a+catchProcess (Process m) h =+ Process $ \pid ->+ catchCont (m pid) $ \e ->+ let Process m' = h e in m' pid+ +-- | A computation with finalization part.+finallyProcess :: Process a -> Process b -> Process a+finallyProcess (Process m) (Process m') =+ Process $ \pid ->+ finallyCont (m pid) (m' pid)++-- | Throw the exception with the further exception handling.+-- By some reasons, the standard 'throw' function per se is not handled +-- properly within 'Process' computations, although it will be still +-- handled if it will be hidden under the 'liftIO' function. The problem +-- arises namely with the @throw@ function, not 'IO' computations.+throwProcess :: IOException -> Process a+throwProcess = liftIO . throw+
+ Simulation/Aivika/Internal/Signal.hs view
@@ -0,0 +1,260 @@++-- |+-- Module : Simulation.Aivika.Internal.Signal+-- Copyright : Copyright (c) 2009-2013, 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 signal which we can subscribe handlers to. +-- These handlers can be disposed. The signal is triggered in the +-- current time point actuating the corresponded computations from +-- the handlers. +--++module Simulation.Aivika.Internal.Signal+ (Signal(..),+ SignalSource(..),+ newSignalSource,+ handleSignal_,+ mapSignal,+ mapSignalM,+ apSignal,+ filterSignal,+ filterSignalM,+ emptySignal,+ merge2Signals,+ merge3Signals,+ merge4Signals,+ merge5Signals) where++import Data.IORef+import Data.Monoid++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event++-- | The signal source that can publish its signal.+data SignalSource a =+ SignalSource { publishSignal :: Signal a,+ -- ^ Publish the signal.+ triggerSignal :: a -> Event ()+ -- ^ Trigger the signal actuating + -- all its handlers at the current + -- simulation time point.+ }+ +-- | The signal that can have disposable handlers. +data Signal a =+ Signal { handleSignal :: (a -> Event ()) -> Event (Event ())+ -- ^ Subscribe the handler to the specified + -- signal and return a nested computation + -- that, being applied, unsubscribes the + -- handler from this signal.+ }+ +-- | The queue of signal handlers.+data SignalHandlerQueue a =+ SignalHandlerQueue { queueStart :: IORef (Maybe (SignalHandler a)),+ queueEnd :: IORef (Maybe (SignalHandler a)) }+ +-- | It contains the information about the disposable queue handler.+data SignalHandler a =+ SignalHandler { handlerComp :: a -> Event (),+ handlerPrev :: IORef (Maybe (SignalHandler a)),+ handlerNext :: IORef (Maybe (SignalHandler a)) }++-- | Subscribe the handler to the specified signal.+-- To subscribe the disposable handlers, use function 'handleSignal'.+handleSignal_ :: Signal a -> (a -> Event ()) -> Event ()+handleSignal_ signal h = + do x <- handleSignal signal h+ return ()+ +-- | Create a new signal source.+newSignalSource :: Simulation (SignalSource a)+newSignalSource =+ Simulation $ \r ->+ do start <- newIORef Nothing+ end <- newIORef Nothing+ let queue = SignalHandlerQueue { queueStart = start,+ queueEnd = end }+ signal = Signal { handleSignal = handle }+ source = SignalSource { publishSignal = signal, + triggerSignal = trigger }+ handle h =+ Event $ \p ->+ do x <- enqueueSignalHandler queue h+ return $+ Event $ \p -> dequeueSignalHandler queue x+ trigger a =+ Event $ \p ->+ let h = queueStart queue+ in triggerSignalHandlers h a p+ return source++-- | Trigger all next signal handlers.+triggerSignalHandlers :: IORef (Maybe (SignalHandler a)) -> a -> Point -> IO ()+{-# INLINE triggerSignalHandlers #-}+triggerSignalHandlers r a p =+ do x <- readIORef r+ case x of+ Nothing -> return ()+ Just h ->+ do invokeEvent p $ handlerComp h a+ triggerSignalHandlers (handlerNext h) a p+ +-- | Enqueue the handler and return its representative in the queue. +enqueueSignalHandler :: SignalHandlerQueue a -> (a -> Event ()) -> IO (SignalHandler a)+enqueueSignalHandler q h = + do tail <- readIORef (queueEnd q)+ case tail of+ Nothing ->+ do prev <- newIORef Nothing+ next <- newIORef Nothing+ let handler = SignalHandler { handlerComp = h,+ handlerPrev = prev,+ handlerNext = next }+ writeIORef (queueStart q) (Just handler)+ writeIORef (queueEnd q) (Just handler)+ return handler+ Just x ->+ do prev <- newIORef tail+ next <- newIORef Nothing+ let handler = SignalHandler { handlerComp = h,+ handlerPrev = prev,+ handlerNext = next }+ writeIORef (handlerNext x) (Just handler)+ writeIORef (queueEnd q) (Just handler)+ return handler++-- | Dequeue the handler representative.+dequeueSignalHandler :: SignalHandlerQueue a -> SignalHandler a -> IO ()+dequeueSignalHandler q h = + do prev <- readIORef (handlerPrev h)+ case prev of+ Nothing ->+ do next <- readIORef (handlerNext h)+ case next of+ Nothing ->+ do writeIORef (queueStart q) Nothing+ writeIORef (queueEnd q) Nothing+ Just y ->+ do writeIORef (handlerPrev y) Nothing+ writeIORef (handlerNext h) Nothing+ writeIORef (queueStart q) next+ Just x ->+ do next <- readIORef (handlerNext h)+ case next of+ Nothing ->+ do writeIORef (handlerPrev h) Nothing+ writeIORef (handlerNext x) Nothing+ writeIORef (queueEnd q) prev+ Just y ->+ do writeIORef (handlerPrev h) Nothing+ writeIORef (handlerNext h) Nothing+ writeIORef (handlerPrev y) prev+ writeIORef (handlerNext x) next++instance Functor Signal where+ fmap = mapSignal+ +instance Monoid (Signal a) where + + mempty = emptySignal+ + mappend = merge2Signals+ + mconcat [] = emptySignal+ mconcat [x1] = x1+ mconcat [x1, x2] = merge2Signals x1 x2+ mconcat [x1, x2, x3] = merge3Signals x1 x2 x3+ mconcat [x1, x2, x3, x4] = merge4Signals x1 x2 x3 x4+ mconcat [x1, x2, x3, x4, x5] = merge5Signals x1 x2 x3 x4 x5+ mconcat (x1 : x2 : x3 : x4 : x5 : xs) = + mconcat $ merge5Signals x1 x2 x3 x4 x5 : xs+ +-- | Map the signal according the specified function.+mapSignal :: (a -> b) -> Signal a -> Signal b+mapSignal f m =+ Signal { handleSignal = \h -> + handleSignal m $ h . f }++-- | Filter only those signal values that satisfy to +-- the specified predicate.+filterSignal :: (a -> Bool) -> Signal a -> Signal a+filterSignal p m =+ Signal { handleSignal = \h ->+ handleSignal m $ \a ->+ when (p a) $ h a }+ +-- | Filter only those signal values that satisfy to +-- the specified predicate.+filterSignalM :: (a -> Event Bool) -> Signal a -> Signal a+filterSignalM p m =+ Signal { handleSignal = \h ->+ handleSignal m $ \a ->+ do x <- p a+ when x $ h a }+ +-- | Merge two signals.+merge2Signals :: Signal a -> Signal a -> Signal a+merge2Signals m1 m2 =+ Signal { handleSignal = \h ->+ do x1 <- handleSignal m1 h+ x2 <- handleSignal m2 h+ return $ do { x1; x2 } }++-- | Merge three signals.+merge3Signals :: Signal a -> Signal a -> Signal a -> Signal a+merge3Signals m1 m2 m3 =+ Signal { handleSignal = \h ->+ do x1 <- handleSignal m1 h+ x2 <- handleSignal m2 h+ x3 <- handleSignal m3 h+ return $ do { x1; x2; x3 } }++-- | Merge four signals.+merge4Signals :: Signal a -> Signal a -> Signal a -> + Signal a -> Signal a+merge4Signals m1 m2 m3 m4 =+ Signal { handleSignal = \h ->+ do x1 <- handleSignal m1 h+ x2 <- handleSignal m2 h+ x3 <- handleSignal m3 h+ x4 <- handleSignal m4 h+ return $ do { x1; x2; x3; x4 } }+ +-- | Merge five signals.+merge5Signals :: Signal a -> Signal a -> Signal a -> + Signal a -> Signal a -> Signal a+merge5Signals m1 m2 m3 m4 m5 =+ Signal { handleSignal = \h ->+ do x1 <- handleSignal m1 h+ x2 <- handleSignal m2 h+ x3 <- handleSignal m3 h+ x4 <- handleSignal m4 h+ x5 <- handleSignal m5 h+ return $ do { x1; x2; x3; x4; x5 } }++-- | Compose the signal.+mapSignalM :: (a -> Event b) -> Signal a -> Signal b+mapSignalM f m =+ Signal { handleSignal = \h ->+ handleSignal m (f >=> h) }+ +-- | Transform the signal.+apSignal :: Event (a -> b) -> Signal a -> Signal b+apSignal f m =+ Signal { handleSignal = \h ->+ handleSignal m $ \a -> do { x <- f; h (x a) } }++-- | An empty signal which is never triggered.+emptySignal :: Signal a+emptySignal =+ Signal { handleSignal = \h -> return $ return () }
+ Simulation/Aivika/Internal/Simulation.hs view
@@ -0,0 +1,193 @@++{-# LANGUAGE RecursiveDo #-}++-- |+-- Module : Simulation.Aivika.Internal.Simulation+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The module defines the 'Simulation' monad that represents a simulation run.+-- +module Simulation.Aivika.Internal.Simulation+ (-- * Simulation+ Simulation(..),+ SimulationLift(..),+ invokeSimulation,+ runSimulation,+ runSimulations,+ -- * Error Handling+ catchSimulation,+ finallySimulation,+ throwSimulation,+ -- * Utilities+ simulationIndex,+ simulationCount,+ simulationSpecs,+ simulationEventQueue) where++import qualified Control.Exception as C+import Control.Exception (IOException, throw, finally)++import Control.Monad+import Control.Monad.Trans+import Control.Monad.Fix++import Simulation.Aivika.Internal.Specs++-- | A value in the 'Simulation' monad represents something that+-- doesn't change within the simulation run but may change for+-- other runs.+--+-- This monad is ideal for representing the external+-- parameters for the model, when the Monte-Carlo simulation+-- is used. Also this monad is useful for defining some+-- actions that should occur only once within the simulation run,+-- for example, setting of the integral with help of recursive+-- equations.+--+newtype Simulation a = Simulation (Run -> IO a)++instance Monad Simulation where+ return = returnS+ m >>= k = bindS m k++returnS :: a -> Simulation a+{-# INLINE returnS #-}+returnS a = Simulation (\r -> return a)++bindS :: Simulation a -> (a -> Simulation b) -> Simulation b+{-# INLINE bindS #-}+bindS (Simulation m) k = + Simulation $ \r -> + do a <- m r+ let Simulation m' = k a+ m' r++-- | Run the simulation using the specified specs.+runSimulation :: Simulation a -> Specs -> IO a+runSimulation (Simulation m) sc =+ do q <- newEventQueue sc+ m Run { runSpecs = sc,+ runIndex = 1,+ runCount = 1,+ runEventQueue = q }++-- | Run the given number of simulations using the specified specs, +-- where each simulation is distinguished by its index 'simulationIndex'.+runSimulations :: Simulation a -> Specs -> Int -> [IO a]+runSimulations (Simulation m) sc runs = map f [1 .. runs]+ where f i = do q <- newEventQueue sc+ m Run { runSpecs = sc,+ runIndex = i,+ runCount = runs,+ runEventQueue = q }++-- | Return the run index for the current simulation.+simulationIndex :: Simulation Int+simulationIndex = Simulation $ return . runIndex++-- | Return the number of simulations currently run.+simulationCount :: Simulation Int+simulationCount = Simulation $ return . runCount++-- | Return the simulation specs.+simulationSpecs :: Simulation Specs+simulationSpecs = Simulation $ return . runSpecs++-- | Return the event queue.+simulationEventQueue :: Simulation EventQueue+simulationEventQueue = Simulation $ return . runEventQueue++instance Functor Simulation where+ fmap = liftMS++instance Eq (Simulation a) where+ x == y = error "Can't compare simulation runs." ++instance Show (Simulation a) where+ showsPrec _ x = showString "<< Simulation >>"++liftMS :: (a -> b) -> Simulation a -> Simulation b+{-# INLINE liftMS #-}+liftMS f (Simulation x) =+ Simulation $ \r -> do { a <- x r; return $ f a }++liftM2S :: (a -> b -> c) -> Simulation a -> Simulation b -> Simulation c+{-# INLINE liftM2S #-}+liftM2S f (Simulation x) (Simulation y) =+ Simulation $ \r -> do { a <- x r; b <- y r; return $ f a b }++instance (Num a) => Num (Simulation a) where+ x + y = liftM2S (+) x y+ x - y = liftM2S (-) x y+ x * y = liftM2S (*) x y+ negate = liftMS negate+ abs = liftMS abs+ signum = liftMS signum+ fromInteger i = return $ fromInteger i++instance (Fractional a) => Fractional (Simulation a) where+ x / y = liftM2S (/) x y+ recip = liftMS recip+ fromRational t = return $ fromRational t++instance (Floating a) => Floating (Simulation a) where+ pi = return pi+ exp = liftMS exp+ log = liftMS log+ sqrt = liftMS sqrt+ x ** y = liftM2S (**) x y+ sin = liftMS sin+ cos = liftMS cos+ tan = liftMS tan+ asin = liftMS asin+ acos = liftMS acos+ atan = liftMS atan+ sinh = liftMS sinh+ cosh = liftMS cosh+ tanh = liftMS tanh+ asinh = liftMS asinh+ acosh = liftMS acosh+ atanh = liftMS atanh++instance MonadIO Simulation where+ liftIO m = Simulation $ const m++-- | A type class to lift the simulation computations to other monads.+class Monad m => SimulationLift m where+ + -- | Lift the specified 'Simulation' computation to another monad.+ liftSimulation :: Simulation a -> m a++instance SimulationLift Simulation where+ liftSimulation = id+ +-- | Exception handling within 'Simulation' computations.+catchSimulation :: Simulation a -> (IOException -> Simulation a) -> Simulation a+catchSimulation (Simulation m) h =+ Simulation $ \r -> + C.catch (m r) $ \e ->+ let Simulation m' = h e in m' r+ +-- | A computation with finalization part like the 'finally' function.+finallySimulation :: Simulation a -> Simulation b -> Simulation a+finallySimulation (Simulation m) (Simulation m') =+ Simulation $ \r ->+ C.finally (m r) (m' r)++-- | Like the standard 'throw' function.+throwSimulation :: IOException -> Simulation a+throwSimulation = throw++-- | Invoke the 'Simulation' computation.+invokeSimulation :: Run -> Simulation a -> IO a+{-# INLINE invokeSimulation #-}+invokeSimulation r (Simulation m) = m r++instance MonadFix Simulation where+ mfix f = + Simulation $ \r ->+ do { rec { a <- invokeSimulation r (f a) }; return a }
+ Simulation/Aivika/Internal/Specs.hs view
@@ -0,0 +1,199 @@++-- |+-- Module : Simulation.Aivika.Internal.Specs+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- It defines the simulation specs and related stuff.+module Simulation.Aivika.Internal.Specs+ (Specs(..),+ Method(..),+ Run(..),+ Point(..),+ EventQueue(..),+ newEventQueue,+ basicTime,+ integIterationBnds,+ integIterationHiBnd,+ integIterationLoBnd,+ integPhaseBnds,+ integPhaseHiBnd,+ integPhaseLoBnd,+ integTimes,+ integPoints,+ integStartPoint,+ integStopPoint,+ pointAt) where++import Data.IORef++import qualified Simulation.Aivika.PriorityQueue as PQ++-- | It defines the simulation specs.+data Specs = Specs { spcStartTime :: Double, -- ^ the start time+ spcStopTime :: Double, -- ^ the stop time+ spcDT :: Double, -- ^ the integration time step+ spcMethod :: Method -- ^ the integration method+ } deriving (Eq, Ord, Show)++-- | It defines the integration method.+data Method = Euler -- ^ Euler's method+ | RungeKutta2 -- ^ the 2nd order Runge-Kutta method+ | RungeKutta4 -- ^ the 4th order Runge-Kutta method+ deriving (Eq, Ord, Show)++-- | It indentifies the simulation run.+data Run = Run { runSpecs :: Specs, -- ^ the simulation specs+ runIndex :: Int, -- ^ the current simulation run index+ runCount :: Int, -- ^ the total number of runs in this experiment+ runEventQueue :: EventQueue -- ^ the event queue+ }++-- | It defines the simulation point appended with the additional information.+data Point = Point { pointSpecs :: Specs, -- ^ the simulation specs+ pointRun :: Run, -- ^ the simulation run+ pointTime :: Double, -- ^ the current time+ pointIteration :: Int, -- ^ the current iteration+ pointPhase :: Int -- ^ the current phase+ }++-- | It represents the event queue.+data EventQueue = EventQueue { queuePQ :: PQ.PriorityQueue (Point -> IO ()),+ -- ^ the underlying priority queue+ queueBusy :: IORef Bool,+ -- ^ whether the queue is currently processing events+ queueTime :: IORef Double+ -- ^ the actual time of the event queue+ }++-- | Create a new event queue by the specified specs.+newEventQueue :: Specs -> IO EventQueue+newEventQueue specs = + do f <- newIORef False+ t <- newIORef $ spcStartTime specs+ pq <- PQ.newQueue+ return EventQueue { queuePQ = pq,+ queueBusy = f,+ queueTime = t }++-- | Returns the integration iterations starting from zero.+integIterations :: Specs -> [Int]+integIterations sc = [i1 .. i2] where+ i1 = 0+ i2 = round ((spcStopTime sc - + spcStartTime sc) / spcDT sc)++-- | Returns the first and last integration iterations.+integIterationBnds :: Specs -> (Int, Int)+integIterationBnds sc = (0, round ((spcStopTime sc - + spcStartTime sc) / spcDT sc))++-- | Returns the first integration iteration, i.e. zero.+integIterationLoBnd :: Specs -> Int+integIterationLoBnd sc = 0++-- | Returns the last integration iteration.+integIterationHiBnd :: Specs -> Int+integIterationHiBnd sc = round ((spcStopTime sc - + spcStartTime sc) / spcDT sc)++-- | Returns the phases for the specified simulation specs starting from zero.+integPhases :: Specs -> [Int]+integPhases sc = + case spcMethod sc of+ Euler -> [0]+ RungeKutta2 -> [0, 1]+ RungeKutta4 -> [0, 1, 2, 3]++-- | Returns the first and last integration phases.+integPhaseBnds :: Specs -> (Int, Int)+integPhaseBnds sc = + case spcMethod sc of+ Euler -> (0, 0)+ RungeKutta2 -> (0, 1)+ RungeKutta4 -> (0, 3)++-- | Returns the first integration phase, i.e. zero.+integPhaseLoBnd :: Specs -> Int+integPhaseLoBnd sc = 0+ +-- | Returns the last integration phase, 0 for Euler's method, 1 for RK2 and 3 for RK4.+integPhaseHiBnd :: Specs -> Int+integPhaseHiBnd sc = + case spcMethod sc of+ Euler -> 0+ RungeKutta2 -> 1+ RungeKutta4 -> 3++-- | Returns a simulation time for the integration point specified by +-- the specs, iteration and phase.+basicTime :: Specs -> Int -> Int -> Double+basicTime sc n ph =+ if ph < 0 then + error "Incorrect phase: basicTime"+ else+ spcStartTime sc + n' * spcDT sc + delta (spcMethod sc) ph + where n' = fromIntegral n+ delta Euler 0 = 0+ delta RungeKutta2 0 = 0+ delta RungeKutta2 1 = spcDT sc+ delta RungeKutta4 0 = 0+ delta RungeKutta4 1 = spcDT sc / 2+ delta RungeKutta4 2 = spcDT sc / 2+ delta RungeKutta4 3 = spcDT sc++-- | Return the integration time values.+integTimes :: Specs -> [Double]+integTimes sc = map t [nl .. nu]+ where (nl, nu) = integIterationBnds sc+ t n = basicTime sc n 0++-- | Return the integration time points.+integPoints :: Run -> [Point]+integPoints r = points+ where sc = runSpecs r+ (nl, nu) = integIterationBnds sc+ points = map point [nl .. nu]+ point n = Point { pointSpecs = sc,+ pointRun = r,+ pointTime = basicTime sc n 0,+ pointIteration = n,+ pointPhase = 0 }++-- | Return the start time point.+integStartPoint :: Run -> Point+integStartPoint r = point nl+ where sc = runSpecs r+ (nl, nu) = integIterationBnds sc+ point n = Point { pointSpecs = sc,+ pointRun = r,+ pointTime = basicTime sc n 0,+ pointIteration = n,+ pointPhase = 0 }++-- | Return the stop time point.+integStopPoint :: Run -> Point+integStopPoint r = point nu+ where sc = runSpecs r+ (nl, nu) = integIterationBnds sc+ point n = Point { pointSpecs = sc,+ pointRun = r,+ pointTime = basicTime sc n 0,+ pointIteration = n,+ pointPhase = 0 }++-- | Return the point at the specified time.+pointAt :: Run -> Double -> Point+pointAt r t = p+ where sc = runSpecs r+ t0 = spcStartTime sc+ dt = spcDT sc+ n = fromIntegral $ floor ((t - t0) / dt)+ p = Point { pointSpecs = sc,+ pointRun = r,+ pointTime = t,+ pointIteration = n,+ pointPhase = -1 }
+ Simulation/Aivika/Parameter.hs view
@@ -0,0 +1,59 @@++-- |+-- Module : Simulation.Aivika.Parameter+-- Copyright : Copyright (c) 2009-2013, 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 parameters of simulation experiments.+--++module Simulation.Aivika.Parameter+ (newParameter,+ newTableParameter,+ newIndexedParameter) where++import Data.Array+import Data.IORef+import qualified Data.Map as M+import Control.Concurrent.MVar++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation++-- | Create a thread-safe parameter that returns always the same value within the simulation run, +-- where the value is recalculated for each new run.+newParameter :: IO a -> IO (Simulation a)+newParameter a = newIndexedParameter $ \_ -> a++-- | Create a thread-safe parameter that returns always the same value within the simulation run,+-- where the value is taken consequently from the specified table based on the number of the +-- current run starting from zero. After all values from the table are used, it takes the first +-- value of the table, then the second one and so on.+newTableParameter :: Array Int a -> IO (Simulation a)+newTableParameter t = newIndexedParameter (\i -> return $ t ! (((i - i1) `mod` n) + i1))+ where (i1, i2) = bounds t+ n = i2 - i1 + 1++-- | Create a thread-safe parameter that returns always the same value within the simulation run, +-- where the value depends on the number of this run starting from zero.+newIndexedParameter :: (Int -> IO a) -> IO (Simulation a)+newIndexedParameter f = + do lock <- newMVar ()+ dict <- newIORef M.empty+ return $ Simulation $ \r ->+ do let i = runIndex r+ m <- readIORef dict+ if M.member i m+ then do let Just v = M.lookup i m+ return v+ else withMVar lock $ + \() -> do { m <- readIORef dict;+ if M.member i m+ then do let Just v = M.lookup i m+ return v+ else do v <- f i+ writeIORef dict $ M.insert i v m+ return v }
+ Simulation/Aivika/Parameter/Random.hs view
@@ -0,0 +1,41 @@++-- |+-- Module : Simulation.Aivika.Parameter.Random+-- Copyright : Copyright (c) 2009-2013, 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 random parameters of simulation experiments.+--++module Simulation.Aivika.Parameter.Random+ (newRandomParameter,+ newNormalParameter) where++import System.Random++import Simulation.Aivika.Simulation+import Simulation.Aivika.Random+import Simulation.Aivika.Parameter++-- | Create a new random parameter distributed uniformly.+-- The value doesn't change within the simulation run but+-- then the value is recalculated for each new run.+newRandomParameter :: Simulation Double -- ^ minimum+ -> Simulation Double -- ^ maximum+ -> IO (Simulation Double)+newRandomParameter min max =+ do x <- newParameter $ getStdRandom random+ return $ min + x * (max - min)++-- | Create a new random parameter distributed normally.+-- The value doesn't change within the simulation run but+-- then the value is recalculated for each new run.+newNormalParameter :: Simulation Double -- ^ mean+ -> Simulation Double -- ^ variance+ -> IO (Simulation Double)+newNormalParameter mu nu =+ do x <- newNormalGen >>= newParameter+ return $ mu + x * nu
+ Simulation/Aivika/Process.hs view
@@ -0,0 +1,47 @@++-- |+-- Module : Simulation.Aivika.Process+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- A value in the 'Process' monad represents a discontinuous process that +-- can suspend in any simulation time point and then resume later in the same +-- or another time point. +-- +-- The process of this type can involve the 'Event', 'Dynamics' and 'Simulation'+-- computations. Moreover, a value in the @Process@ monad can be run within+-- the @Event@ computation.+--+-- A value of the 'ProcessId' type is just an identifier of such a process.+--+module Simulation.Aivika.Process+ (ProcessId,+ Process,+ runProcess,+ runProcessInStartTime,+ runProcessInStopTime,+ enqueueProcess,+ enqueueProcessWithStartTime,+ enqueueProcessWithStopTime,+ newProcessId,+ newProcessIdWithCatch,+ processId,+ holdProcess,+ interruptProcess,+ processInterrupted,+ passivateProcess,+ processPassive,+ reactivateProcess,+ cancelProcess,+ processCanceled,+ catchProcess,+ finallyProcess,+ throwProcess) where++import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Process
Simulation/Aivika/Queue.hs view
@@ -7,101 +7,310 @@ -- Stability : experimental -- Tested with: GHC 7.6.3 ----- An imperative double-linked queue.+-- This module defines a queue that can use the specified strategies. So, having only+-- the 'FCFS', 'LCFS', 'SIRO' and 'StaticPriorities' strategies, you can build+-- 4 x 3 x 4 = 48 different types of the queue, each of them will have its own+-- behavior (below @StaticPriorities@ can be used for input and output only). ---module Simulation.Aivika.Queue - (Queue, - queueNull, +module Simulation.Aivika.Queue+ (Queue,+ queueNull,+ queueFull,+ queueMaxCount, queueCount,- newQueue, - enqueue, - dequeue, - queueFront) where + queueLostCount,+ enqueued,+ dequeued,+ enqueuedButLost,+ newQueue,+ dequeue,+ dequeueWithPriority,+ dequeueWithDynamicPriority,+ tryDequeue,+ enqueue,+ enqueueWithPriority,+ enqueueWithDynamicPriority,+ tryEnqueue,+ enqueueOrLost,+ enqueueOrLost_) where import Data.IORef+ import Control.Monad+import Control.Monad.Trans --- | A cell of the double-linked queue.-data QueueItem a = - QueueItem { qiVal :: a,- qiPrev :: IORef (Maybe (QueueItem a)),- qiNext :: IORef (Maybe (QueueItem a)) }- --- | The 'Queue' type represents an imperative double-linked queue.-data Queue a = - Queue { qHead :: IORef (Maybe (QueueItem a)),- qTail :: IORef (Maybe (QueueItem a)), - qSize :: IORef Int }+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Process+import Simulation.Aivika.Internal.Signal+import Simulation.Aivika.Signal+import Simulation.Aivika.Resource+import Simulation.Aivika.QueueStrategy +-- | Represents the queue using the specified strategies for input @si@,+-- internal storing (in memory) @sm@ and 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 maximum available number of items.+ queueInputStrategy :: si,+ queueMemoryStrategy :: sm,+ queueOutputStrategy :: so,+ queueInputRes :: Resource si qi,+ queueMemory :: qm a,+ queueOutputRes :: Resource so qo,+ queueCountRef :: IORef Int,+ queueLostCountRef :: IORef Int,+ enqueuedSource :: SignalSource a,+ enqueuedButLostSource :: SignalSource a,+ dequeuedSource :: SignalSource a }+ +-- | Create a new queue with the specified strategies and maximum available number of items. +newQueue :: (QueueStrategy si qi,+ QueueStrategy sm qm,+ QueueStrategy so qo) =>+ si+ -- ^ the strategy applied to the input (enqueuing) process+ -> sm+ -- ^ the strategy applied when storing items in the queue+ -> so+ -- ^ the strategy applied to the output (dequeuing) process+ -> Int+ -- ^ the maximum available number of items+ -> Simulation (Queue si qi sm qm so qo a) +newQueue si sm so count =+ do i <- liftIO $ newIORef 0+ l <- liftIO $ newIORef 0+ ri <- newResourceWithCount si count count+ qm <- newStrategyQueue sm+ ro <- newResourceWithCount so count 0+ s1 <- newSignalSource+ s2 <- newSignalSource+ s3 <- newSignalSource+ return Queue { queueMaxCount = count,+ queueInputStrategy = si,+ queueMemoryStrategy = sm,+ queueOutputStrategy = so,+ queueInputRes = ri,+ queueMemory = qm,+ queueOutputRes = ro,+ queueCountRef = i,+ queueLostCountRef = l,+ enqueuedSource = s1,+ enqueuedButLostSource = s2,+ dequeuedSource = s3 }+ -- | Test whether the queue is empty.-queueNull :: Queue a -> IO Bool+queueNull :: Queue si qi sm qm so qo a -> Event Bool queueNull q =- do head <- readIORef (qHead q) - case head of- Nothing -> return True- Just _ -> return False- --- | Return the number of elements in the queue.-queueCount :: Queue a -> IO Int-queueCount q = readIORef (qSize q)---- | Create a new queue.-newQueue :: IO (Queue a)-newQueue =- do head <- newIORef Nothing - tail <- newIORef Nothing- size <- newIORef 0- return Queue { qHead = head, qTail = tail, qSize = size }+ Event $ \p ->+ do n <- readIORef (queueCountRef q)+ return (n == 0) --- | Enqueue a new element.-enqueue :: Queue a -> a -> IO ()-enqueue q v =- do size <- readIORef (qSize q)- writeIORef (qSize q) (size + 1)- head <- readIORef (qHead q)- case head of- Nothing ->- do prev <- newIORef Nothing- next <- newIORef Nothing- let item = Just QueueItem { qiVal = v, - qiPrev = prev, - qiNext = next }- writeIORef (qHead q) item- writeIORef (qTail q) item- Just h ->- do prev <- newIORef Nothing- next <- newIORef head- let item = Just QueueItem { qiVal = v,- qiPrev = prev,- qiNext = next }- writeIORef (qiPrev h) item- writeIORef (qHead q) item+-- | Test whether the queue is full.+queueFull :: Queue si qi sm qm so qo a -> Event Bool+queueFull q =+ Event $ \p ->+ do n <- readIORef (queueCountRef q)+ return (n == queueMaxCount q) --- | Dequeue the first element.-dequeue :: Queue a -> IO ()+-- | Return the queue size.+queueCount :: Queue si qi sm qm so qo a -> Event Int+queueCount q =+ Event $ \p -> readIORef (queueCountRef q)+ +-- | Return the number of lost items.+queueLostCount :: Queue si qi sm qm so qo a -> Event Int+queueLostCount q =+ Event $ \p -> readIORef (queueLostCountRef q)+ +-- | Dequeue suspending the process if the queue is empty.+dequeue :: (DequeueStrategy si qi,+ DequeueStrategy sm qm,+ EnqueueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> Process a+ -- ^ the dequeued value dequeue q =- do tail <- readIORef (qTail q) - case tail of- Nothing ->- error "Empty queue: dequeue"- Just t ->- do size <- readIORef (qSize q)- writeIORef (qSize q) (size - 1)- tail' <- readIORef (qiPrev t)- case tail' of- Nothing ->- do writeIORef (qHead q) Nothing- writeIORef (qTail q) Nothing- Just t' ->- do writeIORef (qiNext t') Nothing- writeIORef (qTail q) tail'+ do requestResource (queueOutputRes q)+ a <- liftEvent $+ strategyDequeue (queueMemoryStrategy q) (queueMemory q)+ releaseResource (queueInputRes q)+ liftEvent $+ triggerSignal (dequeuedSource q) a+ return a+ +-- | Dequeue with the priority suspending the process if the queue is empty.+dequeueWithPriority :: (DequeueStrategy si qi,+ DequeueStrategy sm qm,+ PriorityQueueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> Double+ -- ^ the priority+ -> Process a+ -- ^ the dequeued value+dequeueWithPriority q priority =+ do requestResourceWithPriority (queueOutputRes q) priority+ a <- liftEvent $+ strategyDequeue (queueMemoryStrategy q) (queueMemory q)+ releaseResource (queueInputRes q)+ liftEvent $+ triggerSignal (dequeuedSource q) a+ return a+ +-- | Dequeue with the dynamic priority suspending the process if the queue is empty.+dequeueWithDynamicPriority :: (DequeueStrategy si qi,+ DequeueStrategy sm qm,+ DynamicPriorityQueueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> Event Double+ -- ^ the dynamic priority+ -> Process a+ -- ^ the dequeued value+dequeueWithDynamicPriority q priority =+ do requestResourceWithDynamicPriority (queueOutputRes q) priority+ a <- liftEvent $+ strategyDequeue (queueMemoryStrategy q) (queueMemory q)+ releaseResource (queueInputRes q)+ liftEvent $+ triggerSignal (dequeuedSource q) a+ return a+ +-- | Try to dequeue from the queue immediately. +tryDequeue :: (DequeueStrategy si qi,+ DequeueStrategy sm qm)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> Event (Maybe a)+ -- ^ the dequeued value of 'Nothing'+tryDequeue q =+ do x <- tryRequestResourceWithinEvent (queueOutputRes q)+ if x + then do a <- strategyDequeue (queueMemoryStrategy q) (queueMemory q)+ releaseResourceWithinEvent (queueInputRes q)+ triggerSignal (dequeuedSource q) a+ return $ Just a+ else return Nothing --- | Return the first element.-queueFront :: Queue a -> IO a-queueFront q =- do tail <- readIORef (qTail q)- case tail of- Nothing ->- error "Empty queue: front"- Just t ->- return $ qiVal t+-- | Enqueue the item suspending the process if the queue is full. +enqueue :: (EnqueueStrategy si qi,+ EnqueueStrategy sm qm,+ DequeueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> a+ -- ^ the item to enqueue+ -> Process ()+enqueue q a =+ do requestResource (queueInputRes q)+ liftEvent $+ strategyEnqueue (queueMemoryStrategy q) (queueMemory q) a+ releaseResource (queueOutputRes q)+ liftEvent $+ triggerSignal (enqueuedSource q) a+ +-- | Enqueue with the priority the item suspending the process if the queue is full. +enqueueWithPriority :: (PriorityQueueStrategy si qi,+ EnqueueStrategy sm qm,+ DequeueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> Double+ -- ^ the priority+ -> a+ -- ^ the item to enqueue+ -> Process ()+enqueueWithPriority q priority a =+ do requestResourceWithPriority (queueInputRes q) priority+ liftEvent $+ strategyEnqueue (queueMemoryStrategy q) (queueMemory q) a+ releaseResource (queueOutputRes q)+ liftEvent $+ triggerSignal (enqueuedSource q) a+ +-- | Enqueue with the dynamic priority the item suspending the process if the queue is full. +enqueueWithDynamicPriority :: (DynamicPriorityQueueStrategy si qi,+ EnqueueStrategy sm qm,+ DequeueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> Event Double+ -- ^ the dynamic priority+ -> a+ -- ^ the item to enqueue+ -> Process ()+enqueueWithDynamicPriority q priority a =+ do requestResourceWithDynamicPriority (queueInputRes q) priority+ liftEvent $+ strategyEnqueue (queueMemoryStrategy q) (queueMemory q) a+ releaseResource (queueOutputRes q)+ liftEvent $+ triggerSignal (enqueuedSource q) a+ +-- | Try to enqueue the item. Return 'False' in the monad if the queue is full.+tryEnqueue :: (EnqueueStrategy sm qm,+ DequeueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> a+ -- ^ the item which we try to enqueue+ -> Event Bool+tryEnqueue q a =+ do x <- tryRequestResourceWithinEvent (queueInputRes q)+ if x + then do strategyEnqueue (queueMemoryStrategy q) (queueMemory q) a+ releaseResourceWithinEvent (queueOutputRes q)+ triggerSignal (enqueuedSource q) a+ return True+ else return False++-- | Try to enqueue the item. If the queue is full then the item will be lost+-- and 'False' will be returned.+enqueueOrLost :: (EnqueueStrategy sm qm,+ DequeueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> a+ -- ^ the item which we try to enqueue+ -> Event Bool+enqueueOrLost q a =+ do x <- tryRequestResourceWithinEvent (queueInputRes q)+ if x+ then do strategyEnqueue (queueMemoryStrategy q) (queueMemory q) a+ releaseResourceWithinEvent (queueOutputRes q)+ triggerSignal (enqueuedSource q) a+ return True+ else do liftIO $ modifyIORef (queueLostCountRef q) $ (+) 1+ triggerSignal (enqueuedButLostSource q) a+ return False++-- | Try to enqueue the item. If the queue is full then the item will be lost.+enqueueOrLost_ :: (EnqueueStrategy sm qm,+ DequeueStrategy so qo)+ => Queue si qi sm qm so qo a+ -- ^ the queue+ -> a+ -- ^ the item which we try to enqueue+ -> Event ()+enqueueOrLost_ q a =+ do x <- enqueueOrLost q a+ return ()++-- | Return a signal that notifies when any item is enqueued.+enqueued :: Queue si qi sm qm so qo a -> Signal a+enqueued q = publishSignal (enqueuedSource q)++-- | Return a signal which notifies that the item was lost when +-- attempting to add it to the full queue with help of+-- 'enqueueOrLost' or 'enqueueOrLost_'.+enqueuedButLost :: Queue si qi sm qm so qo a -> Signal a+enqueuedButLost q = publishSignal (enqueuedButLostSource q)++-- | Return a signal that notifies when any item is dequeued.+dequeued :: Queue si qi sm qm so qo a -> Signal a+dequeued q = publishSignal (dequeuedSource q)
+ Simulation/Aivika/QueueStrategy.hs view
@@ -0,0 +1,154 @@++{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}++-- |+-- Module : Simulation.Aivika.QueueStrategy+-- Copyright : Copyright (c) 2009-2013, 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 queue strategies.+--+module Simulation.Aivika.QueueStrategy+ (QueueStrategy(..),+ DequeueStrategy(..),+ EnqueueStrategy(..),+ PriorityQueueStrategy(..),+ DynamicPriorityQueueStrategy(..),+ FCFS(..),+ LCFS(..),+ SIRO(..),+ StaticPriorities(..)) where++import System.Random+import Control.Monad.Trans++import Simulation.Aivika.Simulation+import Simulation.Aivika.Event+import Simulation.Aivika.DoubleLinkedList+import qualified Simulation.Aivika.PriorityQueue as PQ+import qualified Simulation.Aivika.Vector as V++-- | Defines the basic queue strategy.+class QueueStrategy s q | s -> q where++ -- | Create a new queue by the specified strategy.+ newStrategyQueue :: s -> Simulation (q i)++ -- | Test whether the queue is empty.+ strategyQueueNull :: s -> q i -> Event Bool++-- | Defines a strategy with support of the dequeuing operation.+class QueueStrategy s q => DequeueStrategy s q | s -> q where++ -- | Dequeue the front element and return it.+ strategyDequeue :: s -> q i -> Event i++-- | It defines a strategy when we can enqueue a single element.+class DequeueStrategy s q => EnqueueStrategy s q | s -> q where++ -- | Enqueue an element.+ strategyEnqueue :: s -> q i -> i -> Event ()++-- | It defines a strategy when we can enqueue an element with the specified priority.+class DequeueStrategy s q => PriorityQueueStrategy s q | s -> q where++ -- | Enqueue an element with the specified priority.+ strategyEnqueueWithPriority :: s -> q i -> Double -> i -> Event ()++-- | It defines a strategy when we can enqueue an element with the dynamic priority.+class DequeueStrategy s q => DynamicPriorityQueueStrategy s q | s -> q where++ -- | Enqueue an element with the specified priority.+ strategyEnqueueWithDynamicPriority :: s -> q i -> Event Double -> i -> Event ()++-- | Strategy: First Come - First Served (FCFS).+data FCFS = FCFS++-- | Strategy: Last Come - First Served (LCFS)+data LCFS = LCFS++-- | Strategy: Service in Random Order (SIRO).+data SIRO = SIRO++-- | Strategy: Static Priorities. It uses the priority queue.+data StaticPriorities = StaticPriorities++instance QueueStrategy FCFS DoubleLinkedList where++ newStrategyQueue s = liftIO newList++ strategyQueueNull s q = liftIO $ listNull q++instance DequeueStrategy FCFS DoubleLinkedList where++ strategyDequeue s q =+ liftIO $+ do i <- listFirst q+ listRemoveFirst q+ return i++instance EnqueueStrategy FCFS DoubleLinkedList where++ strategyEnqueue s q i = liftIO $ listAddLast q i++instance QueueStrategy LCFS DoubleLinkedList where++ newStrategyQueue s = liftIO newList+ + strategyQueueNull s q = liftIO $ listNull q++instance DequeueStrategy LCFS DoubleLinkedList where++ strategyDequeue s q =+ liftIO $+ do i <- listFirst q+ listRemoveFirst q+ return i++instance EnqueueStrategy LCFS DoubleLinkedList where++ strategyEnqueue s q i = liftIO $ listInsertFirst q i++instance QueueStrategy StaticPriorities PQ.PriorityQueue where++ newStrategyQueue s = liftIO PQ.newQueue++ strategyQueueNull s q = liftIO $ PQ.queueNull q++instance DequeueStrategy StaticPriorities PQ.PriorityQueue where++ strategyDequeue s q =+ liftIO $+ do (_, i) <- PQ.queueFront q+ PQ.dequeue q+ return i++instance PriorityQueueStrategy StaticPriorities PQ.PriorityQueue where++ strategyEnqueueWithPriority s q p i = liftIO $ PQ.enqueue q p i++instance QueueStrategy SIRO V.Vector where++ newStrategyQueue s = liftIO V.newVector++ strategyQueueNull s q =+ liftIO $+ do n <- V.vectorCount q+ return (n == 0)++instance DequeueStrategy SIRO V.Vector where++ strategyDequeue s q =+ liftIO $+ do n <- V.vectorCount q+ i <- getStdRandom (randomR (0, n - 1))+ x <- V.readVector q i+ V.vectorDeleteAt q i+ return x++instance EnqueueStrategy SIRO V.Vector where++ strategyEnqueue s q i = liftIO $ V.appendVector q i
+ Simulation/Aivika/Random.hs view
@@ -0,0 +1,53 @@++-- |+-- Module : Simulation.Aivika.Random+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- Below are defined some random functions.+--+module Simulation.Aivika.Random + (newNormalGen) where++import System.Random+import Data.IORef++-- | Createa a normal random number generator with mean 0 and variance 1.+newNormalGen :: IO (IO Double)+newNormalGen =+ do nextRef <- newIORef 0.0+ flagRef <- newIORef False+ xi1Ref <- newIORef 0.0+ xi2Ref <- newIORef 0.0+ psiRef <- newIORef 0.0+ let loop =+ do psi <- readIORef psiRef+ if (psi >= 1.0) || (psi == 0.0)+ then do g1 <- getStdRandom random+ g2 <- getStdRandom random+ let xi1 = 2.0 * g1 - 1.0+ xi2 = 2.0 * g2 - 1.0+ psi = xi1 * xi1 + xi2 * xi2+ writeIORef xi1Ref xi1+ writeIORef xi2Ref xi2+ writeIORef psiRef psi+ loop+ else writeIORef psiRef $ sqrt (- 2.0 * log psi / psi)+ return $+ do flag <- readIORef flagRef+ if flag+ then do writeIORef flagRef False+ readIORef nextRef+ else do writeIORef xi1Ref 0.0+ writeIORef xi2Ref 0.0+ writeIORef psiRef 0.0+ loop+ xi1 <- readIORef xi1Ref+ xi2 <- readIORef xi2Ref+ psi <- readIORef psiRef+ writeIORef flagRef True+ writeIORef nextRef $ xi2 * psi+ return $ xi1 * psi
+ Simulation/Aivika/Ref.hs view
@@ -0,0 +1,69 @@++-- |+-- Module : Simulation.Aivika.Ref+-- Copyright : Copyright (c) 2009-2013, 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 an updatable reference that depends on the event queue.+--+module Simulation.Aivika.Ref+ (Ref,+ refChanged,+ refChanged_,+ newRef,+ readRef,+ writeRef,+ modifyRef) where++import Data.IORef+import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Signal+import Simulation.Aivika.Signal++-- | The 'Ref' type represents a mutable variable similar to the 'IORef' variable +-- but only dependent on the event queue, which allows synchronizing the reference+-- with the model explicitly through the 'Event' monad.+data Ref a = + Ref { refValue :: IORef a, + refChangedSource :: SignalSource a }++-- | Create a new reference.+newRef :: a -> Simulation (Ref a)+newRef a =+ do x <- liftIO $ newIORef a+ s <- newSignalSource+ return Ref { refValue = x, + refChangedSource = s }+ +-- | Read the value of a reference.+readRef :: Ref a -> Event a+readRef r = Event $ \p -> readIORef (refValue r)++-- | Write a new value into the reference.+writeRef :: Ref a -> a -> Event ()+writeRef r a = Event $ \p -> + do a `seq` writeIORef (refValue r) a+ invokeEvent p $ triggerSignal (refChangedSource r) a++-- | Mutate the contents of the reference.+modifyRef :: Ref a -> (a -> a) -> Event ()+modifyRef r f = Event $ \p -> + do a <- readIORef (refValue r)+ let b = f a+ b `seq` writeIORef (refValue r) b+ invokeEvent p $ triggerSignal (refChangedSource r) b++-- | Return a signal that notifies about every change of the reference state.+refChanged :: Ref a -> Signal a+refChanged v = publishSignal (refChangedSource v)++-- | Return a signal that notifies about every change of the reference state.+refChanged_ :: Ref a -> Signal ()+refChanged_ r = mapSignal (const ()) $ refChanged r
+ Simulation/Aivika/Resource.hs view
@@ -0,0 +1,270 @@++-- |+-- Module : Simulation.Aivika.Resource+-- Copyright : Copyright (c) 2009-2013, 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 a limited resource which can be acquired and +-- then released by the discontinuous process 'Process'.+--+module Simulation.Aivika.Resource+ (Resource,+ newResource,+ newResourceWithCount,+ resourceMaxCount,+ resourceCount,+ requestResource,+ requestResourceWithPriority,+ requestResourceWithDynamicPriority,+ tryRequestResourceWithinEvent,+ releaseResource,+ releaseResourceWithinEvent,+ usingResource,+ usingResourceWithPriority,+ usingResourceWithDynamicPriority) where++import Data.IORef+import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Cont+import Simulation.Aivika.Internal.Process++import Simulation.Aivika.QueueStrategy++-- | Represents a limited resource.+data Resource s q = + Resource { resourceStrategy :: s,+ resourceMaxCount :: Int,+ -- ^ Return the maximum count of the resource.+ resourceCountRef :: IORef Int, + resourceWaitList :: q (ContParams ())}++instance Eq (Resource s q) where+ x == y = resourceCountRef x == resourceCountRef y -- unique references++-- | Create a new resource with the specified queue strategy and maximum count.+newResource :: QueueStrategy s q+ => s+ -- ^ the strategy for managing the queuing requests+ -> Int+ -- ^ the maximum count of the resource+ -> Simulation (Resource s q)+newResource s maxCount =+ Simulation $ \r ->+ do countRef <- newIORef maxCount+ waitList <- invokeSimulation r $ newStrategyQueue s+ return Resource { resourceStrategy = s,+ resourceMaxCount = maxCount,+ resourceCountRef = countRef,+ resourceWaitList = waitList }++-- | Create a new resource with the specified queue strategy, maximum and initial count.+newResourceWithCount :: QueueStrategy s q+ => s+ -- ^ the strategy for managing the queuing requests+ -> Int+ -- ^ the maximum count of the resource+ -> Int+ -- ^ the initial count of the resource+ -> Simulation (Resource s q)+newResourceWithCount s maxCount count = do+ when (count < 0) $+ error $+ "The resource count cannot be negative: " +++ "newResourceWithCount."+ when (count > maxCount) $+ error $+ "The resource count cannot be greater than " +++ "its maximum value: newResourceWithCount."+ Simulation $ \r ->+ do countRef <- newIORef count+ waitList <- invokeSimulation r $ newStrategyQueue s+ return Resource { resourceStrategy = s,+ resourceMaxCount = maxCount,+ resourceCountRef = countRef,+ resourceWaitList = waitList }++-- | Return the current count of the resource.+resourceCount :: Resource s q -> Event Int+resourceCount r =+ Event $ \p -> readIORef (resourceCountRef r)++-- | Request for the resource decreasing its count in case of success,+-- otherwise suspending the discontinuous process until some other +-- process releases the resource.+requestResource :: EnqueueStrategy s q+ => Resource s q+ -- ^ the requested resource+ -> Process ()+requestResource r =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do a <- readIORef (resourceCountRef r)+ if a == 0 + then invokeEvent p $+ strategyEnqueue (resourceStrategy r) (resourceWaitList r) c+ else do let a' = a - 1+ a' `seq` writeIORef (resourceCountRef r) a'+ invokeEvent p $ resumeCont c ()++-- | Request with the priority for the resource decreasing its count+-- in case of success, otherwise suspending the discontinuous process+-- until some other process releases the resource.+requestResourceWithPriority :: PriorityQueueStrategy s q+ => Resource s q+ -- ^ the requested resource+ -> Double+ -- ^ the priority+ -> Process ()+requestResourceWithPriority r priority =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do a <- readIORef (resourceCountRef r)+ if a == 0 + then invokeEvent p $+ strategyEnqueueWithPriority (resourceStrategy r) (resourceWaitList r) priority c+ else do let a' = a - 1+ a' `seq` writeIORef (resourceCountRef r) a'+ invokeEvent p $ resumeCont c ()++-- | Request with the dynamic priority for the resource decreasing its count+-- in case of success, otherwise suspending the discontinuous process+-- until some other process releases the resource.+requestResourceWithDynamicPriority :: DynamicPriorityQueueStrategy s q+ => Resource s q+ -- ^ the requested resource+ -> Event Double+ -- ^ the dynamic priority+ -> Process ()+requestResourceWithDynamicPriority r priority =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do a <- readIORef (resourceCountRef r)+ if a == 0 + then invokeEvent p $+ strategyEnqueueWithDynamicPriority (resourceStrategy r) (resourceWaitList r) priority c+ else do let a' = a - 1+ a' `seq` writeIORef (resourceCountRef r) a'+ invokeEvent p $ resumeCont c ()++-- | Release the resource increasing its count and resuming one of the+-- previously suspended processes as possible.+releaseResource :: DequeueStrategy s q+ => Resource s q+ -- ^ the resource to release+ -> Process ()+releaseResource r = + Process $ \_ ->+ Cont $ \c ->+ Event $ \p ->+ do invokeEvent p $ releaseResourceWithinEvent r+ invokeEvent p $ resumeCont c ()++-- | Release the resource increasing its count and resuming one of the+-- previously suspended processes as possible.+releaseResourceWithinEvent :: DequeueStrategy s q+ => Resource s q+ -- ^ the resource to release+ -> Event ()+releaseResourceWithinEvent r =+ Event $ \p ->+ do a <- readIORef (resourceCountRef r)+ let a' = a + 1+ when (a' > resourceMaxCount r) $+ error $+ "The resource count cannot be greater than " +++ "its maximum value: releaseResourceWithinEvent."+ f <- invokeEvent p $+ strategyQueueNull (resourceStrategy r) (resourceWaitList r)+ if f + then a' `seq` writeIORef (resourceCountRef r) a'+ else do c <- invokeEvent p $+ strategyDequeue (resourceStrategy r) (resourceWaitList r)+ invokeEvent p $ enqueueEvent (pointTime p) $+ Event $ \p ->+ do z <- contCanceled c+ if z+ then do invokeEvent p $ releaseResourceWithinEvent r+ invokeEvent p $ resumeCont c ()+ else invokeEvent p $ resumeCont c ()++-- | Try to request for the resource decreasing its count in case of success+-- and returning 'True' in the 'Event' monad; otherwise, returning 'False'.+tryRequestResourceWithinEvent :: Resource s q+ -- ^ the resource which we try to request for+ -> Event Bool+tryRequestResourceWithinEvent r =+ Event $ \p ->+ do a <- readIORef (resourceCountRef r)+ if a == 0 + then return False+ else do let a' = a - 1+ a' `seq` writeIORef (resourceCountRef r) a'+ return True+ +-- | Acquire the resource, perform some action and safely release the resource +-- in the end, even if the 'IOException' was raised within the action. +-- The process identifier must be created with support of exception +-- handling, i.e. with help of function 'newProcessIdWithCatch'. Unfortunately,+-- such processes are slower than those that are created with help of+-- other function 'newProcessId'.+usingResource :: EnqueueStrategy s q+ => Resource s q+ -- ^ the resource we are going to request for and then release in the end+ -> Process a+ -- ^ the action we are going to apply having the resource+ -> Process a+ -- ^ the result of the action+usingResource r m =+ do requestResource r+ finallyProcess m $ releaseResource r++-- | Acquire the resource with the specified priority, perform some action and+-- safely release the resource in the end, even if the 'IOException' was raised+-- within the action. The process identifier must be created with support of exception +-- handling, i.e. with help of function 'newProcessIdWithCatch'. Unfortunately,+-- such processes are slower than those that are created with help of+-- other function 'newProcessId'.+usingResourceWithPriority :: PriorityQueueStrategy s q+ => Resource s q+ -- ^ the resource we are going to request for and then+ -- release in the end+ -> Double+ -- ^ the priority+ -> Process a+ -- ^ the action we are going to apply having the resource+ -> Process a+ -- ^ the result of the action+usingResourceWithPriority r priority m =+ do requestResourceWithPriority r priority+ finallyProcess m $ releaseResource r++-- | Acquire the resource with the dynamic priority, perform some action and+-- safely release the resource in the end, even if the 'IOException' was raised+-- within the action. The process identifier must be created with support of exception +-- handling, i.e. with help of function 'newProcessIdWithCatch'. Unfortunately,+-- such processes are slower than those that are created with help of+-- other function 'newProcessId'.+usingResourceWithDynamicPriority :: DynamicPriorityQueueStrategy s q+ => Resource s q+ -- ^ the resource we are going to request for and then+ -- release in the end+ -> Event Double+ -- ^ the dynamic priority+ -> Process a+ -- ^ the action we are going to apply having the resource+ -> Process a+ -- ^ the result of the action+usingResourceWithDynamicPriority r priority m =+ do requestResourceWithDynamicPriority r priority+ finallyProcess m $ releaseResource r
+ Simulation/Aivika/Signal.hs view
@@ -0,0 +1,137 @@++-- |+-- Module : Simulation.Aivika.Signal+-- Copyright : Copyright (c) 2009-2013, 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 signal which we can subscribe handlers to. +-- These handlers can be disposed. The signal is triggered in the +-- current time point actuating the corresponded computations from +-- the handlers. +--+module Simulation.Aivika.Signal+ (Signal(..),+ handleSignal_,+ SignalSource,+ newSignalSource,+ publishSignal,+ triggerSignal,+ awaitSignal,+ mapSignal,+ mapSignalM,+ apSignal,+ filterSignal,+ filterSignalM,+ emptySignal,+ merge2Signals,+ merge3Signals,+ merge4Signals,+ merge5Signals,+ newSignalInTimes,+ newSignalInIntegTimes,+ newSignalInStartTime,+ newSignalInStopTime,+ SignalHistory,+ signalHistorySignal,+ newSignalHistory,+ readSignalHistory) where++import Data.IORef+import Data.Array++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Signal+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Cont+import Simulation.Aivika.Internal.Process++import qualified Simulation.Aivika.Vector as V+import qualified Simulation.Aivika.Vector.Unboxed as UV++-- | Await the signal.+awaitSignal :: Signal a -> Process a+awaitSignal signal =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do r <- newIORef Nothing+ h <- invokeEvent p $+ handleSignal signal $ + \a -> Event $ + \p -> do x <- readIORef r+ case x of+ Nothing ->+ error "The signal was lost: awaitSignal."+ Just x ->+ do invokeEvent p x+ invokeEvent p $ resumeCont c a+ writeIORef r $ Just h+ +-- | Represents the history of the signal values.+data SignalHistory a =+ SignalHistory { signalHistorySignal :: Signal a, + -- ^ The signal for which the history is created.+ signalHistoryTimes :: UV.Vector Double,+ signalHistoryValues :: V.Vector a }++-- | 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 ->+ Event $ \p ->+ do liftIO $ UV.appendVector ts (pointTime p)+ liftIO $ V.appendVector xs a+ return SignalHistory { signalHistorySignal = signal,+ signalHistoryTimes = ts,+ signalHistoryValues = xs }+ +-- | Read the history of signal values.+readSignalHistory :: SignalHistory a -> Event (Array Int Double, Array Int a)+readSignalHistory history =+ do xs <- liftIO $ UV.freezeVector (signalHistoryTimes history)+ ys <- liftIO $ V.freezeVector (signalHistoryValues history)+ return (xs, ys) + +-- | Trigger the signal with the current time.+triggerSignalWithCurrentTime :: SignalSource Double -> Event ()+triggerSignalWithCurrentTime s =+ Event $ \p -> invokeEvent p $ triggerSignal s (pointTime p)++-- | Return a signal that is triggered in the specified time points.+newSignalInTimes :: [Double] -> Event (Signal Double)+newSignalInTimes xs =+ do s <- liftSimulation newSignalSource+ enqueueEventWithTimes xs $ triggerSignalWithCurrentTime s+ return $ publishSignal s+ +-- | Return a signal that is triggered in the integration time points.+-- It should be called with help of 'runEventInStartTime'.+newSignalInIntegTimes :: Event (Signal Double)+newSignalInIntegTimes =+ do s <- liftSimulation newSignalSource+ enqueueEventWithIntegTimes $ triggerSignalWithCurrentTime s+ return $ publishSignal s+ +-- | Return a signal that is triggered in the start time.+-- It should be called with help of 'runEventInStartTime'.+newSignalInStartTime :: Event (Signal Double)+newSignalInStartTime =+ do s <- liftSimulation newSignalSource+ enqueueEventWithStartTime $ triggerSignalWithCurrentTime s+ return $ publishSignal s++-- | Return a signal that is triggered in the stop time.+newSignalInStopTime :: Event (Signal Double)+newSignalInStopTime =+ do s <- liftSimulation newSignalSource+ enqueueEventWithStopTime $ triggerSignalWithCurrentTime s+ return $ publishSignal s
+ Simulation/Aivika/Simulation.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.Simulation+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The module defines the 'Simulation' monad that represents a simulation run.+-- +module Simulation.Aivika.Simulation+ (-- * Simulation+ Simulation,+ SimulationLift(..),+ runSimulation,+ runSimulations,+ -- * Error Handling+ catchSimulation,+ finallySimulation,+ throwSimulation,+ -- * Utilities+ simulationIndex,+ simulationCount,+ simulationSpecs) where++import Simulation.Aivika.Internal.Simulation
+ Simulation/Aivika/Specs.hs view
@@ -0,0 +1,25 @@++-- |+-- Module : Simulation.Aivika.Specs+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- It defines the simulation specs and functions for this data type.+module Simulation.Aivika.Specs+ (-- * Simulation Specs+ Specs(..),+ Method(..),+ -- * Auxiliary Functions+ basicTime,+ integIterationBnds,+ integIterationHiBnd,+ integIterationLoBnd,+ integPhaseBnds,+ integPhaseHiBnd,+ integPhaseLoBnd,+ integTimes) where++import Simulation.Aivika.Internal.Specs
+ Simulation/Aivika/SystemDynamics.hs view
@@ -0,0 +1,620 @@++{-# LANGUAGE BangPatterns, RecursiveDo #-}++-- |+-- Module : Simulation.Aivika.SystemDynamics+-- Copyright : Copyright (c) 2009-2013, 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 integrals and other functions of System Dynamics.+--++module Simulation.Aivika.SystemDynamics+ (-- * Equality and Ordering+ (.==.),+ (./=.),+ (.<.),+ (.>=.),+ (.>.),+ (.<=.),+ maxDynamics,+ minDynamics,+ ifDynamics,+ -- * Ordinary Differential Equations+ integ,+ smoothI,+ smooth,+ smooth3I,+ smooth3,+ smoothNI,+ smoothN,+ delay1I,+ delay1,+ delay3I,+ delay3,+ delayNI,+ delayN,+ forecast,+ trend,+ -- * Difference Equations+ sumDynamics,+ -- * Table Functions+ lookupDynamics,+ lookupStepwiseDynamics,+ -- * Discrete Functions+ delay,+ -- * Financial Functions+ npv,+ npve) where++import Data.Array+import Data.Array.IO.Safe+import Data.IORef+import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics+import Simulation.Aivika.Dynamics.Interpolate+import Simulation.Aivika.Dynamics.Memo.Unboxed+import Simulation.Aivika.Unboxed++--+-- Equality and Ordering+--++-- | Compare for equality.+(.==.) :: (Eq a) => Dynamics a -> Dynamics a -> Dynamics Bool+(.==.) = liftM2 (==)++-- | Compare for inequality.+(./=.) :: (Eq a) => Dynamics a -> Dynamics a -> Dynamics Bool+(./=.) = liftM2 (/=)++-- | Compare for ordering.+(.<.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool+(.<.) = liftM2 (<)++-- | Compare for ordering.+(.>=.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool+(.>=.) = liftM2 (>=)++-- | Compare for ordering.+(.>.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool+(.>.) = liftM2 (>)++-- | Compare for ordering.+(.<=.) :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics Bool+(.<=.) = liftM2 (<=)++-- | Return the maximum.+maxDynamics :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics a+maxDynamics = liftM2 max++-- | Return the minimum.+minDynamics :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics a+minDynamics = liftM2 min++-- | Implement the if-then-else operator.+ifDynamics :: Dynamics Bool -> Dynamics a -> Dynamics a -> Dynamics a+ifDynamics cond x y =+ do a <- cond+ if a then x else y++--+-- Ordinary Differential Equations+--++integEuler :: Dynamics Double+ -> Dynamics Double + -> Dynamics Double + -> Point -> IO Double+integEuler (Dynamics f) (Dynamics i) (Dynamics y) p = + case pointIteration p of+ 0 -> + i p+ n -> do + let sc = pointSpecs p+ ty = basicTime sc (n - 1) 0+ py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }+ a <- y py+ b <- f py+ let !v = a + spcDT (pointSpecs p) * b+ return v++integRK2 :: Dynamics Double+ -> Dynamics Double+ -> Dynamics Double+ -> Point -> IO Double+integRK2 (Dynamics f) (Dynamics i) (Dynamics y) p =+ case pointPhase p of+ 0 -> case pointIteration p of+ 0 ->+ i p+ n -> do+ let sc = pointSpecs p+ ty = basicTime sc (n - 1) 0+ t1 = ty+ t2 = basicTime sc (n - 1) 1+ py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }+ p1 = py+ p2 = p { pointTime = t2, pointIteration = n - 1, pointPhase = 1 }+ vy <- y py+ k1 <- f p1+ k2 <- f p2+ let !v = vy + spcDT sc / 2.0 * (k1 + k2)+ return v+ 1 -> do+ let sc = pointSpecs p+ n = pointIteration p+ ty = basicTime sc n 0+ t1 = ty+ py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }+ p1 = py+ vy <- y py+ k1 <- f p1+ let !v = vy + spcDT sc * k1+ return v+ _ -> + error "Incorrect phase: integRK2"++integRK4 :: Dynamics Double+ -> Dynamics Double+ -> Dynamics Double+ -> Point -> IO Double+integRK4 (Dynamics f) (Dynamics i) (Dynamics y) p =+ case pointPhase p of+ 0 -> case pointIteration p of+ 0 -> + i p+ n -> do+ let sc = pointSpecs p+ ty = basicTime sc (n - 1) 0+ t1 = ty+ t2 = basicTime sc (n - 1) 1+ t3 = basicTime sc (n - 1) 2+ t4 = basicTime sc (n - 1) 3+ py = p { pointTime = ty, pointIteration = n - 1, pointPhase = 0 }+ p1 = py+ p2 = p { pointTime = t2, pointIteration = n - 1, pointPhase = 1 }+ p3 = p { pointTime = t3, pointIteration = n - 1, pointPhase = 2 }+ p4 = p { pointTime = t4, pointIteration = n - 1, pointPhase = 3 }+ vy <- y py+ k1 <- f p1+ k2 <- f p2+ k3 <- f p3+ k4 <- f p4+ let !v = vy + spcDT sc / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4)+ return v+ 1 -> do+ let sc = pointSpecs p+ n = pointIteration p+ ty = basicTime sc n 0+ t1 = ty+ py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }+ p1 = py+ vy <- y py+ k1 <- f p1+ let !v = vy + spcDT sc / 2.0 * k1+ return v+ 2 -> do+ let sc = pointSpecs p+ n = pointIteration p+ ty = basicTime sc n 0+ t2 = basicTime sc n 1+ py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }+ p2 = p { pointTime = t2, pointIteration = n, pointPhase = 1 }+ vy <- y py+ k2 <- f p2+ let !v = vy + spcDT sc / 2.0 * k2+ return v+ 3 -> do+ let sc = pointSpecs p+ n = pointIteration p+ ty = basicTime sc n 0+ t3 = basicTime sc n 2+ py = p { pointTime = ty, pointIteration = n, pointPhase = 0 }+ p3 = p { pointTime = t3, pointIteration = n, pointPhase = 2 }+ vy <- y py+ k3 <- f p3+ let !v = vy + spcDT sc * k3+ return v+ _ -> + error "Incorrect phase: integRK4"++-- | Return an integral with the specified derivative and initial value.+--+-- To create a loopback, you should use the recursive do-notation.+-- It allows defining the differential equations unordered as+-- in mathematics:+--+-- @+-- model :: Simulation [Double]+-- model = +-- mdo a <- integ (- ka * a) 100+-- b <- integ (ka * a - kb * b) 0+-- c <- integ (kb * b) 0+-- let ka = 1+-- kb = 1+-- runDynamicsInStopTime $ sequence [a, b, c]+-- @+integ :: Dynamics Double -- ^ the derivative+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the integral+integ diff i =+ mdo y <- memoDynamics z+ z <- Simulation $ \r ->+ case spcMethod (runSpecs r) of+ Euler -> return $ Dynamics $ integEuler diff i y+ RungeKutta2 -> return $ Dynamics $ integRK2 diff i y+ RungeKutta4 -> return $ Dynamics $ integRK4 diff i y+ return y++-- | Return the first order exponential smooth.+--+-- To create a loopback, you should use the recursive do-notation+-- with help of which the function itself is defined:+--+-- @+-- smoothI x t i =+-- mdo y <- integ ((x - y) \/ t) i+-- return y+-- @ +smoothI :: Dynamics Double -- ^ the value to smooth over time+ -> Dynamics Double -- ^ time+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the first order exponential smooth+smoothI x t i =+ mdo y <- integ ((x - y) / t) i+ return y++-- | Return the first order exponential smooth.+--+-- This is a simplified version of the 'smoothI' function+-- without specifing the initial value.+smooth :: Dynamics Double -- ^ the value to smooth over time+ -> Dynamics Double -- ^ time+ -> Simulation (Dynamics Double) -- ^ the first order exponential smooth+smooth x t = smoothI x t x++-- | Return the third order exponential smooth.+--+-- To create a loopback, you should use the recursive do-notation+-- with help of which the function itself is defined:+--+-- @+-- smooth3I x t i =+-- mdo y <- integ ((s2 - y) \/ t') i+-- s2 <- integ ((s1 - s2) \/ t') i+-- s1 <- integ ((x - s1) \/ t') i+-- let t' = t \/ 3.0+-- return y+-- @ +smooth3I :: Dynamics Double -- ^ the value to smooth over time+ -> Dynamics Double -- ^ time+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the third order exponential smooth+smooth3I x t i =+ mdo y <- integ ((s2 - y) / t') i+ s2 <- integ ((s1 - s2) / t') i+ s1 <- integ ((x - s1) / t') i+ let t' = t / 3.0+ return y++-- | Return the third order exponential smooth.+-- +-- This is a simplified version of the 'smooth3I' function+-- without specifying the initial value.+smooth3 :: Dynamics Double -- ^ the value to smooth over time+ -> Dynamics Double -- ^ time+ -> Simulation (Dynamics Double) -- ^ the third order exponential smooth+smooth3 x t = smooth3I x t x++-- | Return the n'th order exponential smooth.+--+-- The result is not discrete in that sense that it may change within the integration time+-- interval depending on the integration method used. Probably, you should apply+-- the 'discreteDynamics' function to the result if you want to achieve an effect when+-- the value is not changed within the time interval, which is used sometimes.+smoothNI :: Dynamics Double -- ^ the value to smooth over time+ -> Dynamics Double -- ^ time+ -> Int -- ^ the order+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the n'th order exponential smooth+smoothNI x t n i =+ mdo s <- forM [1 .. n] $ \k ->+ if k == 1+ then integ ((x - a ! 1) / t') i+ else integ ((a ! (k - 1) - a ! k) / t') i+ let a = listArray (1, n) s + t' = t / fromIntegral n+ return $ a ! n++-- | Return the n'th order exponential smooth.+--+-- This is a simplified version of the 'smoothNI' function+-- without specifying the initial value.+smoothN :: Dynamics Double -- ^ the value to smooth over time+ -> Dynamics Double -- ^ time+ -> Int -- ^ the order+ -> Simulation (Dynamics Double) -- ^ the n'th order exponential smooth+smoothN x t n = smoothNI x t n x++-- | Return the first order exponential delay.+--+-- To create a loopback, you should use the recursive do-notation+-- with help of which the function itself is defined:+--+-- @+-- delay1I x t i =+-- mdo y <- integ (x - y \/ t) (i * t)+-- return $ y \/ t+-- @ +delay1I :: Dynamics Double -- ^ the value to conserve+ -> Dynamics Double -- ^ time+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the first order exponential delay+delay1I x t i =+ mdo y <- integ (x - y / t) (i * t)+ return $ y / t++-- | Return the first order exponential delay.+--+-- This is a simplified version of the 'delay1I' function+-- without specifying the initial value.+delay1 :: Dynamics Double -- ^ the value to conserve+ -> Dynamics Double -- ^ time+ -> Simulation (Dynamics Double) -- ^ the first order exponential delay+delay1 x t = delay1I x t x++-- | Return the third order exponential delay.+delay3I :: Dynamics Double -- ^ the value to conserve+ -> Dynamics Double -- ^ time+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the third order exponential delay+delay3I x t i =+ mdo y <- integ (s2 / t' - y / t') (i * t')+ s2 <- integ (s1 / t' - s2 / t') (i * t')+ s1 <- integ (x - s1 / t') (i * t')+ let t' = t / 3.0+ return $ y / t' ++-- | Return the third order exponential delay.+--+-- This is a simplified version of the 'delay3I' function+-- without specifying the initial value.+delay3 :: Dynamics Double -- ^ the value to conserve+ -> Dynamics Double -- ^ time+ -> Simulation (Dynamics Double) -- ^ the third order exponential delay+delay3 x t = delay3I x t x++-- | Return the n'th order exponential delay.+delayNI :: Dynamics Double -- ^ the value to conserve+ -> Dynamics Double -- ^ time+ -> Int -- ^ the order+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the n'th order exponential delay+delayNI x t n i =+ mdo s <- forM [1 .. n] $ \k ->+ if k == 1+ then integ (x - (a ! 1) / t') (i * t')+ else integ ((a ! (k - 1)) / t' - (a ! k) / t') (i * t')+ let a = listArray (1, n) s+ t' = t / fromIntegral n+ return $ (a ! n) / t'++-- | Return the n'th order exponential delay.+--+-- This is a simplified version of the 'delayNI' function+-- without specifying the initial value.+delayN :: Dynamics Double -- ^ the value to conserve+ -> Dynamics Double -- ^ time+ -> Int -- ^ the order+ -> Simulation (Dynamics Double) -- ^ the n'th order exponential delay+delayN x t n = delayNI x t n x++-- | Return the forecast.+--+-- The function has the following definition:+--+-- @+-- forecast x at hz =+-- do y <- smooth x at+-- return $ x * (1.0 + (x \/ y - 1.0) \/ at * hz)+-- @+forecast :: Dynamics Double -- ^ the value to forecast+ -> Dynamics Double -- ^ the average time+ -> Dynamics Double -- ^ the time horizon+ -> Simulation (Dynamics Double) -- ^ the forecast+forecast x at hz =+ do y <- smooth x at+ return $ x * (1.0 + (x / y - 1.0) / at * hz)++-- | Return the trend.+--+-- The function has the following definition:+--+-- @+-- trend x at i =+-- do y <- smoothI x at (x \/ (1.0 + i * at))+-- return $ (x \/ y - 1.0) \/ at+-- @+trend :: Dynamics Double -- ^ the value for which the trend is calculated+ -> Dynamics Double -- ^ the average time+ -> Dynamics Double -- ^ the initial value+ -> Simulation (Dynamics Double) -- ^ the fractional change rate+trend x at i =+ do y <- smoothI x at (x / (1.0 + i * at))+ return $ (x / y - 1.0) / at++--+-- Difference Equations+--++-- | Retun the sum for the difference equation.+-- It is like an integral returned by the 'integ' function, only now+-- the difference is used instead of derivative.+--+-- As usual, to create a loopback, you should use the recursive do-notation.+sumDynamics :: (Num a, Unboxed a)+ => Dynamics a -- ^ the difference+ -> Dynamics a -- ^ the initial value+ -> Simulation (Dynamics a) -- ^ the sum+sumDynamics (Dynamics diff) (Dynamics i) =+ mdo y <- memoDynamics z+ z <- Simulation $ \r ->+ return $ Dynamics $ \p ->+ case pointIteration p of+ 0 -> i p+ n -> do + let Dynamics m = y+ sc = pointSpecs p+ ty = basicTime sc (n - 1) 0+ py = p { pointTime = ty, + pointIteration = n - 1, + pointPhase = 0 }+ a <- m py+ b <- diff py+ let !v = a + b+ return v+ return y++--+-- Table Functions+--++-- | Lookup @x@ in a table of pairs @(x, y)@ using linear interpolation.+lookupDynamics :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double+lookupDynamics (Dynamics m) tbl =+ Dynamics (\p -> do a <- m p; return $ find first last a) where+ (first, last) = bounds tbl+ find left right x =+ if left > right then+ error "Incorrect index: table"+ else+ let index = (left + 1 + right) `div` 2+ x1 = fst $ tbl ! index+ in if x1 <= x then + let y | index < right = find index right x+ | right == last = snd $ tbl ! right+ | otherwise = + let x2 = fst $ tbl ! (index + 1)+ y1 = snd $ tbl ! index+ y2 = snd $ tbl ! (index + 1)+ in y1 + (y2 - y1) * (x - x1) / (x2 - x1) + in y+ else+ let y | left < index = find left (index - 1) x+ | left == first = snd $ tbl ! left+ | otherwise = error "Incorrect index: table"+ in y++-- | Lookup @x@ in a table of pairs @(x, y)@ using stepwise function.+lookupStepwiseDynamics :: Dynamics Double+ -> Array Int (Double, Double)+ -> Dynamics Double+lookupStepwiseDynamics (Dynamics m) tbl =+ Dynamics (\p -> do a <- m p; return $ find first last a) where+ (first, last) = bounds tbl+ find left right x =+ if left > right then+ error "Incorrect index: table"+ else+ let index = (left + 1 + right) `div` 2+ x1 = fst $ tbl ! index+ in if x1 <= x then + let y | index < right = find index right x+ | right == last = snd $ tbl ! right+ | otherwise = snd $ tbl ! right+ in y+ else+ let y | left < index = find left (index - 1) x+ | left == first = snd $ tbl ! left+ | otherwise = error "Incorrect index: table"+ in y++--+-- Discrete Functions+--++-- | Return the delayed value.+--+-- If you want to apply the result recursively in some loopback then you+-- should use one of the memoization functions such as 'memoDynamics'+-- and 'memo0Dynamics'. +delay :: Dynamics a -- ^ the value to delay+ -> Dynamics Double -- ^ the lag time+ -> Dynamics a -- ^ the initial value+ -> Dynamics a -- ^ the delayed value+delay (Dynamics x) (Dynamics d) (Dynamics i) = Dynamics r + where+ r p = do + let t = pointTime p+ sc = pointSpecs p+ n = pointIteration p+ a <- d p+ let t' = t - a+ n' = fromIntegral $ floor $ (t' - spcStartTime sc) / spcDT sc+ y | n' < 0 = i $ p { pointTime = spcStartTime sc,+ pointIteration = 0, + pointPhase = 0 }+ | n' < n = x $ p { pointTime = t',+ pointIteration = n',+ pointPhase = -1 }+ | n' > n = error $+ "Cannot return the future data: delay. " +++ "The lag time cannot be negative."+ | otherwise = error $+ "Cannot return the current data: delay. " +++ "The lag time is too small."+ y++--+-- Financial Functions+--++-- | Return the Net Present Value (NPV) of the stream computed using the specified+-- discount rate, the initial value and some factor (usually 1).+--+-- It is defined in the following way:+--+-- @+-- npv stream rate init factor =+-- mdo df <- integ (- df * rate) 1+-- accum <- integ (stream * df) init+-- return $ (accum + dt * stream * df) * factor+-- @+npv :: Dynamics Double -- ^ the stream+ -> Dynamics Double -- ^ the discount rate+ -> Dynamics Double -- ^ the initial value+ -> Dynamics Double -- ^ factor+ -> Simulation (Dynamics Double) -- ^ the Net Present Value (NPV)+npv stream rate init factor =+ mdo df <- integ (- df * rate) 1+ accum <- integ (stream * df) init+ return $ (accum + dt * stream * df) * factor++-- | Return the Net Present Value End of period (NPVE) of the stream computed+-- using the specified discount rate, the initial value and some factor.+--+-- It is defined in the following way:+--+-- @+-- npve stream rate init factor =+-- mdo df <- integ (- df * rate \/ (1 + rate * dt)) (1 \/ (1 + rate * dt))+-- accum <- integ (stream * df) init+-- return $ (accum + dt * stream * df) * factor+-- @+npve :: Dynamics Double -- ^ the stream+ -> Dynamics Double -- ^ the discount rate+ -> Dynamics Double -- ^ the initial value+ -> Dynamics Double -- ^ factor+ -> Simulation (Dynamics Double) -- ^ the Net Present Value End (NPVE)+npve stream rate init factor =+ mdo df <- integ (- df * rate / (1 + rate * dt)) (1 / (1 + rate * dt))+ accum <- integ (stream * df) init+ return $ (accum + dt * stream * df) * factor
− Simulation/Aivika/UVector.hs
@@ -1,188 +0,0 @@--{-# LANGUAGE FlexibleContexts #-}---- |--- Module : Simulation.Aivika.UVector--- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>--- License : BSD3--- Maintainer : David Sorokin <david.sorokin@gmail.com>--- Stability : experimental--- Tested with: GHC 7.6.3------ An imperative unboxed vector.----module Simulation.Aivika.UVector- (UVector, - newVector, - copyVector, - vectorCount, - appendVector, - readVector, - writeVector, - vectorBinarySearch,- vectorInsert,- vectorDeleteAt,- vectorIndex,- freezeVector) where --import Data.Array-import Data.Array.MArray.Safe-import Data.Array.IO.Safe-import Data.IORef-import Control.Monad---- | Represents an unboxed resizable vector.-data UVector a = UVector { vectorArrayRef :: IORef (IOUArray Int a),- vectorCountRef :: IORef Int, - vectorCapacityRef :: IORef Int }---- | Create a new vector.-newVector :: MArray IOUArray a IO => IO (UVector a)-newVector = - do array <- newArray_ (0, 4 - 1)- arrayRef <- newIORef array- countRef <- newIORef 0- capacityRef <- newIORef 4- return UVector { vectorArrayRef = arrayRef,- vectorCountRef = countRef,- vectorCapacityRef = capacityRef }---- | Copy the vector.-copyVector :: (MArray IOUArray a IO) => UVector a -> IO (UVector a)-copyVector vector =- do array <- readIORef (vectorArrayRef vector)- count <- readIORef (vectorCountRef vector)- array' <- newArray_ (0, count - 1)- arrayRef' <- newIORef array'- countRef' <- newIORef count- capacityRef' <- newIORef count- forM_ [0 .. count - 1] $ \i ->- do x <- readArray array i- writeArray array' i x- return UVector { vectorArrayRef = arrayRef',- vectorCountRef = countRef',- vectorCapacityRef = capacityRef' }---- | Ensure that the vector has the specified capacity.-vectorEnsureCapacity :: MArray IOUArray a IO => UVector a -> Int -> IO ()-vectorEnsureCapacity vector capacity =- do capacity' <- readIORef (vectorCapacityRef vector)- when (capacity' < capacity) $- do array' <- readIORef (vectorArrayRef vector)- count' <- readIORef (vectorCountRef vector)- let capacity'' = max (2 * capacity') capacity- array'' <- newArray_ (0, capacity'' - 1)- forM_ [0 .. count' - 1] $ \i ->- do x <- readArray array' i- writeArray array'' i x- writeIORef (vectorArrayRef vector) array''- writeIORef (vectorCapacityRef vector) capacity''- --- | Return the element count.-vectorCount :: MArray IOUArray a IO => UVector a -> IO Int-vectorCount vector = readIORef (vectorCountRef vector)- --- | Add the specified element to the end of the vector.-appendVector :: MArray IOUArray a IO => UVector a -> a -> IO () -appendVector vector item =- do count <- readIORef (vectorCountRef vector)- vectorEnsureCapacity vector (count + 1)- array <- readIORef (vectorArrayRef vector)- writeArray array count item- writeIORef (vectorCountRef vector) (count + 1)- --- | Read a value from the vector, where indices are started from 0.-readVector :: MArray IOUArray a IO => UVector a -> Int -> IO a-readVector vector index =- do array <- readIORef (vectorArrayRef vector)- readArray array index- --- | Set an array item at the specified index which is started from 0.-writeVector :: MArray IOUArray a IO => UVector a -> Int -> a -> IO ()-writeVector vector index item =- do array <- readIORef (vectorArrayRef vector)- writeArray array index item- -vectorBinarySearch' :: (MArray IOUArray a IO, Ord a) => - IOUArray Int a -> a -> Int -> Int -> IO Int-vectorBinarySearch' array item left right =- if left > right - then return $ - (right + 1) - 1- else- do let index = (left + right) `div` 2- curr <- readArray array index- if item < curr - then vectorBinarySearch' array item left (index - 1)- else if item == curr- then return index- else vectorBinarySearch' array item (index + 1) right- --- | Return the index of the specified element using binary search; otherwise, --- a negated insertion index minus one: 0 -> -0 - 1, ..., i -> -i - 1, ....-vectorBinarySearch :: (MArray IOUArray a IO, Ord a) => UVector a -> a -> IO Int-vectorBinarySearch vector item =- do array <- readIORef (vectorArrayRef vector)- count <- readIORef (vectorCountRef vector)- vectorBinarySearch' array item 0 (count - 1)--freezeVector :: (MArray IOUArray a IO) => UVector a -> IO (Array Int a)-freezeVector vector = - do vector' <- copyVector vector- array <- readIORef (vectorArrayRef vector')- freeze array- - --- | Insert the element in the vector at the specified index.-vectorInsert :: (MArray IOUArray a IO) => UVector a -> Int -> a -> IO () -vectorInsert vector index item =- do count <- readIORef (vectorCountRef vector)- when (index < 0) $- error $- "Index cannot be " ++- "negative: vectorInsert."- when (index > count) $- error $- "Index cannot be greater " ++- "than the count: vectorInsert."- vectorEnsureCapacity vector (count + 1)- array <- readIORef (vectorArrayRef vector)- forM_ [count, count - 1 .. index + 1] $ \i ->- do x <- readArray array (i - 1)- writeArray array i x- writeArray array index item- writeIORef (vectorCountRef vector) (count + 1)- --- | Delete the element at the specified index.-vectorDeleteAt :: (MArray IOUArray a IO) => UVector a -> Int -> IO ()-vectorDeleteAt vector index =- do count <- readIORef (vectorCountRef vector)- when (index < 0) $- error $- "Index cannot be " ++- "negative: vectorDeleteAt."- when (index >= count) $- error $- "Index must be less " ++- "than the count: vectorDeleteAt."- array <- readIORef (vectorArrayRef vector)- forM_ [index, index + 1 .. count - 2] $ \i ->- do x <- readArray array (i + 1)- writeArray array i x- writeArray array (count - 1) undefined- writeIORef (vectorCountRef vector) (count - 1)- --- | Return the index of the item or -1. -vectorIndex :: (MArray IOUArray a IO, Eq a) => - UVector a -> a -> IO Int-vectorIndex vector item =- do count <- readIORef (vectorCountRef vector)- array <- readIORef (vectorArrayRef vector)- let loop index =- if index >= count- then return $ -1- else do x <- readArray array index- if item == x- then return index- else loop $ index + 1- loop 0-
+ Simulation/Aivika/Unboxed.hs view
@@ -0,0 +1,43 @@++{-# LANGUAGE FlexibleContexts #-}++-- |+-- Module : Simulation.Aivika.Unboxed+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- The 'Unboxed' class allows creating unboxed arrays in monad 'IO'.+--++module Simulation.Aivika.Unboxed+ (Unboxed(..)) where++import Data.Array+import Data.Array.IO.Safe+import Data.Int +import Data.Word ++-- | The type which values can be contained in an unboxed array.+class MArray IOUArray e IO => Unboxed e where++ -- | Create an unboxed array with default values.+ newUnboxedArray_ :: Ix i => (i, i) -> IO (IOUArray i e)+ newUnboxedArray_ = newArray_++instance Unboxed Bool +instance Unboxed Char +instance Unboxed Double +instance Unboxed Float +instance Unboxed Int +instance Unboxed Int8 +instance Unboxed Int16 +instance Unboxed Int32 +instance Unboxed Int64 +instance Unboxed Word +instance Unboxed Word8 +instance Unboxed Word16 +instance Unboxed Word32 +instance Unboxed Word64
+ Simulation/Aivika/Var.hs view
@@ -0,0 +1,158 @@++-- |+-- Module : Simulation.Aivika.Var+-- Copyright : Copyright (c) 2009-2013, 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 a variable that is bound up with the event queue and +-- that keeps the history of changes storing the values in an array, which+-- allows using the variable in differential and difference equations under+-- some conditions.+--+module Simulation.Aivika.Var+ (Var,+ varChanged,+ varChanged_,+ newVar,+ readVar,+ writeVar,+ modifyVar,+ freezeVar) where++import Data.Array+import Data.Array.IO.Safe+import Data.IORef++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Signal+import Simulation.Aivika.Signal++import qualified Simulation.Aivika.Vector as V+import qualified Simulation.Aivika.Vector.Unboxed as UV++-- | Like the 'Ref' reference but keeps the history of changes in +-- different time points. The 'Var' variable is usually safe in the hybrid +-- simulation, for example, when it can be used in the differential or+-- difference equations unless you update the variable twice in the+-- same integration time point. Only this variable is much slower than+-- the reference.+data Var a = + Var { varXS :: UV.Vector Double, + varYS :: V.Vector a,+ varChangedSource :: SignalSource a }+ +-- | Create a new variable.+newVar :: a -> Simulation (Var a)+newVar a =+ Simulation $ \r ->+ do xs <- UV.newVector+ ys <- V.newVector+ UV.appendVector xs $ spcStartTime $ runSpecs r+ V.appendVector ys a+ s <- invokeSimulation r newSignalSource+ return Var { varXS = xs,+ varYS = ys, + varChangedSource = s }++-- | 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+-- in the same integration time point; otherwise, different values can be returned+-- for the same point.+readVar :: Var a -> Event a+readVar v =+ Event $ \p ->+ do let xs = varXS v+ ys = varYS v+ t = pointTime p+ count <- UV.vectorCount xs+ let i = count - 1+ x <- UV.readVector xs i+ if x <= t + then V.readVector ys i+ else do i <- UV.vectorBinarySearch xs t+ if i >= 0+ then V.readVector ys i+ else V.readVector ys $ - (i + 1) - 1++-- | Write a new value into the variable.+writeVar :: Var a -> a -> Event ()+writeVar v a =+ Event $ \p ->+ do let xs = varXS v+ ys = varYS v+ t = pointTime p+ s = varChangedSource v+ count <- UV.vectorCount xs+ let i = count - 1+ x <- UV.readVector xs i+ if t < x + then error "Cannot update the past data: writeVar."+ else if t == x+ then V.writeVector ys i $! a+ else do UV.appendVector xs t+ V.appendVector ys $! a+ invokeEvent p $ triggerSignal s a++-- | Mutate the contents of the variable.+modifyVar :: Var a -> (a -> a) -> Event ()+modifyVar v f =+ Event $ \p ->+ do let xs = varXS v+ ys = varYS v+ t = pointTime p+ s = varChangedSource v+ count <- UV.vectorCount xs+ let i = count - 1+ x <- UV.readVector xs i+ if t < x+ then error "Cannot update the past data: modifyVar."+ else if t == x+ then do a <- V.readVector ys i+ let b = f a+ V.writeVector ys i $! b+ invokeEvent p $ triggerSignal s b+ else do i <- UV.vectorBinarySearch xs t+ if i >= 0+ then do a <- V.readVector ys i+ let b = f a+ UV.appendVector xs t+ V.appendVector ys $! b+ invokeEvent p $ triggerSignal s b+ else do a <- V.readVector ys $ - (i + 1) - 1+ let b = f a+ UV.appendVector xs t+ V.appendVector ys $! b+ invokeEvent p $ triggerSignal s b++-- | Freeze the variable and return in arrays the time points and corresponded +-- values when the variable had changed in different time points: (1) the last+-- actual value per each time point is provided and (2) the time points are+-- sorted in ascending order.+--+-- If you need to get all changes including those ones that correspond to the same+-- simulation time points then you can use the 'newSignalHistory' function passing+-- in the 'varChanged' signal to it and then call function 'readSignalHistory'.+freezeVar :: Var a -> Event (Array Int Double, Array Int a)+freezeVar v =+ Event $ \p ->+ do xs <- UV.freezeVector (varXS v)+ ys <- V.freezeVector (varYS v)+ return (xs, ys)+ +-- | Return a signal that notifies about every change of the variable state.+varChanged :: Var a -> Signal a+varChanged v = publishSignal (varChangedSource v)++-- | Return a signal that notifies about every change of the variable state.+varChanged_ :: Var a -> Signal ()+varChanged_ v = mapSignal (const ()) $ varChanged v
+ Simulation/Aivika/Var/Unboxed.hs view
@@ -0,0 +1,158 @@++-- |+-- Module : Simulation.Aivika.Var.Unboxed+-- Copyright : Copyright (c) 2009-2013, 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 an unboxed variable that is bound up with the event queue and +-- that keeps the history of changes storing the values in an unboxed array, which+-- allows using the variable in differential and difference equations under+-- some conditions.+--+module Simulation.Aivika.Var.Unboxed+ (Var,+ varChanged,+ varChanged_,+ newVar,+ readVar,+ writeVar,+ modifyVar,+ freezeVar) where++import Data.Array+import Data.Array.IO.Safe+import Data.IORef++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Signal+import Simulation.Aivika.Signal+import Simulation.Aivika.Unboxed++import qualified Simulation.Aivika.Vector.Unboxed as UV++-- | Like the 'Ref' reference but keeps the history of changes in +-- different time points. The 'Var' variable is usually safe in the hybrid +-- simulation, for example, when it can be used in the differential or+-- difference equations unless you update the variable twice in the+-- same integration time point. Only this variable is much slower than+-- the reference.+data Var a = + Var { varXS :: UV.Vector Double, + varYS :: UV.Vector a,+ varChangedSource :: SignalSource a }+ +-- | Create a new variable.+newVar :: Unboxed a => a -> Simulation (Var a)+newVar a =+ Simulation $ \r ->+ do xs <- UV.newVector+ ys <- UV.newVector+ UV.appendVector xs $ spcStartTime $ runSpecs r+ UV.appendVector ys a+ s <- invokeSimulation r newSignalSource+ return Var { varXS = xs,+ varYS = ys, + varChangedSource = s }++-- | 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+-- in the same integration time point; otherwise, different values can be returned+-- for the same point.+readVar :: Unboxed a => Var a -> Event a+readVar v =+ Event $ \p ->+ do let xs = varXS v+ ys = varYS v+ t = pointTime p+ count <- UV.vectorCount xs+ let i = count - 1+ x <- UV.readVector xs i+ if x <= t + then UV.readVector ys i+ else do i <- UV.vectorBinarySearch xs t+ if i >= 0+ then UV.readVector ys i+ else UV.readVector ys $ - (i + 1) - 1++-- | Write a new value into the variable.+writeVar :: Unboxed a => Var a -> a -> Event ()+writeVar v a =+ Event $ \p ->+ do let xs = varXS v+ ys = varYS v+ t = pointTime p+ s = varChangedSource v+ count <- UV.vectorCount xs+ let i = count - 1+ x <- UV.readVector xs i+ if t < x + then error "Cannot update the past data: writeVar."+ else if t == x+ then UV.writeVector ys i $! a+ else do UV.appendVector xs t+ UV.appendVector ys $! a+ invokeEvent p $ triggerSignal s a++-- | Mutate the contents of the variable.+modifyVar :: Unboxed a => Var a -> (a -> a) -> Event ()+modifyVar v f =+ Event $ \p ->+ do let xs = varXS v+ ys = varYS v+ t = pointTime p+ s = varChangedSource v+ count <- UV.vectorCount xs+ let i = count - 1+ x <- UV.readVector xs i+ if t < x+ then error "Cannot update the past data: modifyVar."+ else if t == x+ then do a <- UV.readVector ys i+ let b = f a+ UV.writeVector ys i $! b+ invokeEvent p $ triggerSignal s b+ else do i <- UV.vectorBinarySearch xs t+ if i >= 0+ then do a <- UV.readVector ys i+ let b = f a+ UV.appendVector xs t+ UV.appendVector ys $! b+ invokeEvent p $ triggerSignal s b+ else do a <- UV.readVector ys $ - (i + 1) - 1+ let b = f a+ UV.appendVector xs t+ UV.appendVector ys $! b+ invokeEvent p $ triggerSignal s b++-- | Freeze the variable and return in arrays the time points and corresponded +-- values when the variable had changed in different time points: (1) the last+-- actual value per each time point is provided and (2) the time points are+-- sorted in ascending order.+--+-- If you need to get all changes including those ones that correspond to the same+-- simulation time points then you can use the 'newSignalHistory' function passing+-- in the 'varChanged' signal to it and then call function 'readSignalHistory'.+freezeVar :: Unboxed a => Var a -> Event (Array Int Double, Array Int a)+freezeVar v =+ Event $ \p ->+ do xs <- UV.freezeVector (varXS v)+ ys <- UV.freezeVector (varYS v)+ return (xs, ys)+ +-- | Return a signal that notifies about every change of the variable state.+varChanged :: Var a -> Signal a+varChanged v = publishSignal (varChangedSource v)++-- | Return a signal that notifies about every change of the variable state.+varChanged_ :: Var a -> Signal ()+varChanged_ v = mapSignal (const ()) $ varChanged v
Simulation/Aivika/Vector.hs view
@@ -122,6 +122,7 @@ count <- readIORef (vectorCountRef vector) vectorBinarySearch' array item 0 (count - 1) +-- | Return the elements of the vector in an immutable array. freezeVector :: Vector a -> IO (Array Int a) freezeVector vector = do vector' <- copyVector vector
+ Simulation/Aivika/Vector/Unboxed.hs view
@@ -0,0 +1,186 @@++-- |+-- Module : Simulation.Aivika.Vector.Unboxed+-- Copyright : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 7.6.3+--+-- An imperative unboxed vector.+--+module Simulation.Aivika.Vector.Unboxed+ (Vector, + newVector, + copyVector, + vectorCount, + appendVector, + readVector, + writeVector, + vectorBinarySearch,+ vectorInsert,+ vectorDeleteAt,+ vectorIndex,+ freezeVector) where ++import Data.Array+import Data.Array.MArray.Safe+import Data.Array.IO.Safe+import Data.IORef+import Control.Monad++import Simulation.Aivika.Unboxed++-- | Represents an unboxed resizable vector.+data Vector a = Vector { vectorArrayRef :: IORef (IOUArray Int a),+ vectorCountRef :: IORef Int, + vectorCapacityRef :: IORef Int }++-- | Create a new vector.+newVector :: Unboxed a => IO (Vector a)+newVector = + do array <- newUnboxedArray_ (0, 4 - 1)+ arrayRef <- newIORef array+ countRef <- newIORef 0+ capacityRef <- newIORef 4+ return Vector { vectorArrayRef = arrayRef,+ vectorCountRef = countRef,+ vectorCapacityRef = capacityRef }++-- | Copy the vector.+copyVector :: Unboxed a => Vector a -> IO (Vector a)+copyVector vector =+ do array <- readIORef (vectorArrayRef vector)+ count <- readIORef (vectorCountRef vector)+ array' <- newUnboxedArray_ (0, count - 1)+ arrayRef' <- newIORef array'+ countRef' <- newIORef count+ capacityRef' <- newIORef count+ forM_ [0 .. count - 1] $ \i ->+ do x <- readArray array i+ writeArray array' i x+ return Vector { vectorArrayRef = arrayRef',+ vectorCountRef = countRef',+ vectorCapacityRef = capacityRef' }++-- | Ensure that the vector has the specified capacity.+vectorEnsureCapacity :: Unboxed a => Vector a -> Int -> IO ()+vectorEnsureCapacity vector capacity =+ do capacity' <- readIORef (vectorCapacityRef vector)+ when (capacity' < capacity) $+ do array' <- readIORef (vectorArrayRef vector)+ count' <- readIORef (vectorCountRef vector)+ let capacity'' = max (2 * capacity') capacity+ array'' <- newUnboxedArray_ (0, capacity'' - 1)+ forM_ [0 .. count' - 1] $ \i ->+ do x <- readArray array' i+ writeArray array'' i x+ writeIORef (vectorArrayRef vector) array''+ writeIORef (vectorCapacityRef vector) capacity''+ +-- | Return the element count.+vectorCount :: Unboxed a => Vector a -> IO Int+vectorCount vector = readIORef (vectorCountRef vector)+ +-- | Add the specified element to the end of the vector.+appendVector :: Unboxed a => Vector a -> a -> IO () +appendVector vector item =+ do count <- readIORef (vectorCountRef vector)+ vectorEnsureCapacity vector (count + 1)+ array <- readIORef (vectorArrayRef vector)+ writeArray array count item+ writeIORef (vectorCountRef vector) (count + 1)+ +-- | Read a value from the vector, where indices are started from 0.+readVector :: Unboxed a => Vector a -> Int -> IO a+readVector vector index =+ do array <- readIORef (vectorArrayRef vector)+ readArray array index+ +-- | Set an array item at the specified index which is started from 0.+writeVector :: Unboxed a => Vector a -> Int -> a -> IO ()+writeVector vector index item =+ do array <- readIORef (vectorArrayRef vector)+ writeArray array index item+ +vectorBinarySearch' :: (Unboxed a, Ord a) => IOUArray Int a -> a -> Int -> Int -> IO Int+vectorBinarySearch' array item left right =+ if left > right + then return $ - (right + 1) - 1+ else+ do let index = (left + right) `div` 2+ curr <- readArray array index+ if item < curr + then vectorBinarySearch' array item left (index - 1)+ else if item == curr+ then return index+ else vectorBinarySearch' array item (index + 1) right+ +-- | Return the index of the specified element using binary search; otherwise, +-- a negated insertion index minus one: 0 -> -0 - 1, ..., i -> -i - 1, ....+vectorBinarySearch :: (Unboxed a, Ord a) => Vector a -> a -> IO Int+vectorBinarySearch vector item =+ do array <- readIORef (vectorArrayRef vector)+ count <- readIORef (vectorCountRef vector)+ vectorBinarySearch' array item 0 (count - 1)++-- | Return the elements of the vector in an immutable array.+freezeVector :: Unboxed a => Vector a -> IO (Array Int a)+freezeVector vector = + do vector' <- copyVector vector+ array <- readIORef (vectorArrayRef vector')+ freeze array+ +-- | Insert the element in the vector at the specified index.+vectorInsert :: Unboxed a => Vector a -> Int -> a -> IO () +vectorInsert vector index item =+ do count <- readIORef (vectorCountRef vector)+ when (index < 0) $+ error $+ "Index cannot be " +++ "negative: vectorInsert."+ when (index > count) $+ error $+ "Index cannot be greater " +++ "than the count: vectorInsert."+ vectorEnsureCapacity vector (count + 1)+ array <- readIORef (vectorArrayRef vector)+ forM_ [count, count - 1 .. index + 1] $ \i ->+ do x <- readArray array (i - 1)+ writeArray array i x+ writeArray array index item+ writeIORef (vectorCountRef vector) (count + 1)+ +-- | Delete the element at the specified index.+vectorDeleteAt :: Unboxed a => Vector a -> Int -> IO ()+vectorDeleteAt vector index =+ do count <- readIORef (vectorCountRef vector)+ when (index < 0) $+ error $+ "Index cannot be " +++ "negative: vectorDeleteAt."+ when (index >= count) $+ error $+ "Index must be less " +++ "than the count: vectorDeleteAt."+ array <- readIORef (vectorArrayRef vector)+ forM_ [index, index + 1 .. count - 2] $ \i ->+ do x <- readArray array (i + 1)+ writeArray array i x+ writeArray array (count - 1) undefined+ writeIORef (vectorCountRef vector) (count - 1)+ +-- | Return the index of the item or -1. +vectorIndex :: (Unboxed a, Eq a) => Vector a -> a -> IO Int+vectorIndex vector item =+ do count <- readIORef (vectorCountRef vector)+ array <- readIORef (vectorArrayRef vector)+ let loop index =+ if index >= count+ then return $ -1+ else do x <- readArray array index+ if item == x+ then return index+ else loop $ index + 1+ loop 0+
aivika.cabal view
@@ -1,5 +1,5 @@ name: aivika-version: 0.6.1+version: 0.7 synopsis: A multi-paradigm simulation library description: Aivika is a multi-paradigm simulation library which has @@ -15,8 +15,12 @@ with an ability to resume, suspend and cancel the discontinuous processes; .- * allows working with limited resources;+ * allows working with limited resources (you can define your own behaviour+ or use the predefined queue strategies); .+ * allows customizing the queues (you can define your own behaviour+ or use the predefined queue strategies);+ . * supports the activity-oriented paradigm of DES; . * supports the basic constructs for the agent-based modeling;@@ -36,7 +40,7 @@ * allows gathering statistics in time points; . * hides the technical details in high-level simulation monads- (two of them support the recursive do-notation).+ (three of them support the recursive do-notation). . Aivika itself is a light-weight engine with minimal dependencies. However, it has additional packages Aivika Experiment [1] and @@ -58,7 +62,7 @@ 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. + 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. .@@ -81,9 +85,7 @@ extra-source-files: examples/BassDiffusion.hs examples/ChemicalReaction.hs- examples/ChemicalReactionRec.hs examples/FishBank.hs- examples/FishBankRec.hs examples/MachRep1.hs examples/MachRep1EventDriven.hs examples/MachRep1TimeDriven.hs@@ -98,39 +100,43 @@ library - exposed-modules: Simulation.Aivika.Dynamics- Simulation.Aivika.Dynamics.Agent- Simulation.Aivika.Dynamics.Base- Simulation.Aivika.Dynamics.Cont- Simulation.Aivika.Dynamics.EventQueue- Simulation.Aivika.Dynamics.Parameter- Simulation.Aivika.Dynamics.Process+ exposed-modules: Simulation.Aivika.Agent+ Simulation.Aivika.Cont+ Simulation.Aivika.DoubleLinkedList+ Simulation.Aivika.Dynamics+ Simulation.Aivika.Dynamics.Fold+ Simulation.Aivika.Dynamics.Interpolate+ Simulation.Aivika.Dynamics.Memo+ Simulation.Aivika.Dynamics.Memo.Unboxed Simulation.Aivika.Dynamics.Random- Simulation.Aivika.Dynamics.Ref- Simulation.Aivika.Dynamics.Resource- Simulation.Aivika.Dynamics.Simulation- Simulation.Aivika.Dynamics.SystemDynamics- Simulation.Aivika.Dynamics.UVar- Simulation.Aivika.Dynamics.Var- Simulation.Aivika.Dynamics.FIFO- Simulation.Aivika.Dynamics.LIFO- Simulation.Aivika.Dynamics.Buffer- Simulation.Aivika.Dynamics.Signal- Simulation.Aivika.Statistics+ Simulation.Aivika.Event+ Simulation.Aivika.Parameter+ Simulation.Aivika.Parameter.Random Simulation.Aivika.PriorityQueue+ Simulation.Aivika.Process Simulation.Aivika.Queue-- other-modules: Simulation.Aivika.Dynamics.Internal.Dynamics- Simulation.Aivika.Dynamics.Internal.Simulation- Simulation.Aivika.Dynamics.Internal.Cont- Simulation.Aivika.Dynamics.Internal.Process- Simulation.Aivika.Dynamics.Internal.Time- Simulation.Aivika.Dynamics.Internal.Memo- Simulation.Aivika.Dynamics.Internal.Interpolate- Simulation.Aivika.Dynamics.Internal.Fold- Simulation.Aivika.Dynamics.Internal.Signal+ Simulation.Aivika.QueueStrategy+ Simulation.Aivika.Random+ Simulation.Aivika.Ref+ Simulation.Aivika.Resource+ Simulation.Aivika.Signal+ Simulation.Aivika.Simulation+ Simulation.Aivika.Specs+ Simulation.Aivika.Statistics+ Simulation.Aivika.SystemDynamics+ Simulation.Aivika.Unboxed+ Simulation.Aivika.Var+ Simulation.Aivika.Var.Unboxed Simulation.Aivika.Vector- Simulation.Aivika.UVector+ Simulation.Aivika.Vector.Unboxed++ other-modules: Simulation.Aivika.Internal.Cont+ Simulation.Aivika.Internal.Dynamics+ Simulation.Aivika.Internal.Event+ Simulation.Aivika.Internal.Process+ Simulation.Aivika.Internal.Signal+ Simulation.Aivika.Internal.Simulation+ Simulation.Aivika.Internal.Specs build-depends: base >= 4.5.0.0 && < 6, mtl >= 2.1.1,@@ -140,6 +146,8 @@ extensions: FlexibleContexts, BangPatterns,- RecursiveDo+ RecursiveDo,+ MultiParamTypeClasses,+ FunctionalDependencies ghc-options: -O2
examples/BassDiffusion.hs view
@@ -4,11 +4,12 @@ import Control.Monad import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation+import Simulation.Aivika.Event import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Agent-import Simulation.Aivika.Dynamics.Ref+import Simulation.Aivika.Agent+import Simulation.Aivika.Ref n = 500 -- the number of agents @@ -35,19 +36,19 @@ personPotentialAdopter :: AgentState, personAdopter :: AgentState } -createPerson :: EventQueue -> Simulation Person -createPerson q = - do agent <- newAgent q+createPerson :: Simulation Person +createPerson = + do agent <- newAgent potentialAdopter <- newState agent adopter <- newState agent return Person { personAgent = agent, personPotentialAdopter = potentialAdopter, personAdopter = adopter } -createPersons :: EventQueue -> Simulation (Array Int Person)-createPersons q =+createPersons :: Simulation (Array Int Person)+createPersons = do list <- forM [1 .. n] $ \i ->- do p <- createPerson q+ do p <- createPerson return (i, p) return $ array (1, n) list @@ -81,23 +82,23 @@ forM_ (elems ps) $ \p -> definePerson p ps potentialAdopters adopters -activatePerson :: Person -> Dynamics ()+activatePerson :: Person -> Event () activatePerson p = activateState (personPotentialAdopter p) -activatePersons :: Array Int Person -> Dynamics ()+activatePersons :: Array Int Person -> Event () activatePersons ps = forM_ (elems ps) $ \p -> activatePerson p model :: Simulation [IO [Int]] model =- do q <- newQueue- potentialAdopters <- newRef q 0- adopters <- newRef q 0- ps <- createPersons q+ do potentialAdopters <- newRef 0+ adopters <- newRef 0+ ps <- createPersons definePersons ps potentialAdopters adopters- runDynamicsInStartTime $+ runEventInStartTime IncludingCurrentEvents $ activatePersons ps runDynamicsInIntegTimes $+ runEvent IncludingCurrentEvents $ do i1 <- readRef potentialAdopters i2 <- readRef adopters return [i1, i2]
examples/ChemicalReaction.hs view
@@ -1,7 +1,10 @@ +{-# LANGUAGE RecursiveDo #-}++import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.SystemDynamics+import Simulation.Aivika.SystemDynamics specs = Specs { spcStartTime = 0, spcStopTime = 13, @@ -9,18 +12,12 @@ spcMethod = RungeKutta4 } model :: Simulation [Double]-model =- do integA <- newInteg 100- integB <- newInteg 0- integC <- newInteg 0- let a = integValue integA- b = integValue integB- c = integValue integC- let ka = 1- kb = 1- integDiff integA (- ka * a)- integDiff integB (ka * a - kb * b)- integDiff integC (kb * b)- runDynamicsInStopTime $ sequence [a, b, c]+model = + mdo a <- integ (- ka * a) 100+ b <- integ (ka * a - kb * b) 0+ c <- integ (kb * b) 0+ let ka = 1+ kb = 1+ runDynamicsInStopTime $ sequence [a, b, c] main = runSimulation model specs >>= print
− examples/ChemicalReactionRec.hs
@@ -1,22 +0,0 @@--{-# LANGUAGE RecursiveDo #-}--import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.SystemDynamics--specs = Specs { spcStartTime = 0, - spcStopTime = 13, - spcDT = 0.01,- spcMethod = RungeKutta4 }--model :: Simulation [Double]-model = - mdo a <- integ (- ka * a) 100- b <- integ (ka * a - kb * b) 0- c <- integ (kb * b) 0- let ka = 1- kb = 1- runDynamicsInStopTime $ sequence [a, b, c]--main = runSimulation model specs >>= print
examples/FishBank.hs view
@@ -1,9 +1,12 @@ +{-# LANGUAGE RecursiveDo #-}+ import Data.Array +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.SystemDynamics+import Simulation.Aivika.SystemDynamics specs = Specs { spcStartTime = 0, spcStopTime = 13, @@ -13,46 +16,37 @@ model :: Simulation Double model =- do fishInteg <- newInteg 1000- shipsInteg <- newInteg 10- totalProfitInteg <- newInteg 0- -- integral values --- let fish = integValue fishInteg- ships = integValue shipsInteg- totalProfit = integValue totalProfitInteg- -- auxiliary values --- let annualProfit = profit- area = 100- carryingCapacity = 1000- catchPerShip = - lookupD density $- listArray (1, 11) [(0.0, -0.048), (1.2, 10.875), (2.4, 17.194), - (3.6, 20.548), (4.8, 22.086), (6.0, 23.344), - (7.2, 23.903), (8.4, 24.462), (9.6, 24.882), - (10.8, 25.301), (12.0, 25.86)]- deathFraction = - lookupD (fish / carryingCapacity) $- listArray (1, 11) [(0.0, 5.161), (0.1, 5.161), (0.2, 5.161), - (0.3, 5.161), (0.4, 5.161), (0.5, 5.161), - (0.6, 5.118), (0.7, 5.247), (0.8, 5.849), - (0.9, 6.151), (10.0, 6.194)]- density = fish / area- fishDeathRate = maxDynamics 0 (fish * deathFraction)- fishHatchRate = maxDynamics 0 (fish * hatchFraction)- fishPrice = 20- fractionInvested = 0.2- hatchFraction = 6- operatingCost = ships * 250- profit = revenue - operatingCost- revenue = totalCatchPerYear * fishPrice- shipBuildingRate = maxDynamics 0 (profit * fractionInvested / shipCost)- shipCost = 300- totalCatchPerYear = maxDynamics 0 (ships * catchPerShip)- -- derivatives --- integDiff fishInteg (fishHatchRate - fishDeathRate - totalCatchPerYear)- integDiff shipsInteg shipBuildingRate- integDiff totalProfitInteg annualProfit- -- results --- runDynamicsInStopTime annualProfit+ mdo let annualProfit = profit+ area = 100+ carryingCapacity = 1000+ catchPerShip = + lookupDynamics density $+ listArray (1, 11) [(0.0, -0.048), (1.2, 10.875), (2.4, 17.194), + (3.6, 20.548), (4.8, 22.086), (6.0, 23.344), + (7.2, 23.903), (8.4, 24.462), (9.6, 24.882), + (10.8, 25.301), (12.0, 25.86)]+ deathFraction = + lookupDynamics (fish / carryingCapacity) $+ listArray (1, 11) [(0.0, 5.161), (0.1, 5.161), (0.2, 5.161), + (0.3, 5.161), (0.4, 5.161), (0.5, 5.161), + (0.6, 5.118), (0.7, 5.247), (0.8, 5.849), + (0.9, 6.151), (10.0, 6.194)]+ density = fish / area+ fish <- integ (fishHatchRate - fishDeathRate - totalCatchPerYear) 1000+ let fishDeathRate = maxDynamics 0 (fish * deathFraction)+ fishHatchRate = maxDynamics 0 (fish * hatchFraction)+ fishPrice = 20+ fractionInvested = 0.2+ hatchFraction = 6+ operatingCost = ships * 250+ profit = revenue - operatingCost+ revenue = totalCatchPerYear * fishPrice+ ships <- integ shipBuildingRate 10+ let shipBuildingRate = maxDynamics 0 (profit * fractionInvested / shipCost)+ shipCost = 300+ totalProfit <- integ annualProfit 0+ let totalCatchPerYear = maxDynamics 0 (ships * catchPerShip)+ -- results --+ runDynamicsInStopTime annualProfit main = runSimulation model specs >>= print
− examples/FishBankRec.hs
@@ -1,51 +0,0 @@--{-# LANGUAGE RecursiveDo #-}--import Data.Array--import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.SystemDynamics--specs = Specs { spcStartTime = 0, - spcStopTime = 13, - spcDT = 0.01,- -- spcDT = 0.000005,- spcMethod = RungeKutta4 }--model :: Simulation Double-model =- mdo let annualProfit = profit- area = 100- carryingCapacity = 1000- catchPerShip = - lookupDynamics density $- listArray (1, 11) [(0.0, -0.048), (1.2, 10.875), (2.4, 17.194), - (3.6, 20.548), (4.8, 22.086), (6.0, 23.344), - (7.2, 23.903), (8.4, 24.462), (9.6, 24.882), - (10.8, 25.301), (12.0, 25.86)]- deathFraction = - lookupDynamics (fish / carryingCapacity) $- listArray (1, 11) [(0.0, 5.161), (0.1, 5.161), (0.2, 5.161), - (0.3, 5.161), (0.4, 5.161), (0.5, 5.161), - (0.6, 5.118), (0.7, 5.247), (0.8, 5.849), - (0.9, 6.151), (10.0, 6.194)]- density = fish / area- fish <- integ (fishHatchRate - fishDeathRate - totalCatchPerYear) 1000- let fishDeathRate = maxDynamics 0 (fish * deathFraction)- fishHatchRate = maxDynamics 0 (fish * hatchFraction)- fishPrice = 20- fractionInvested = 0.2- hatchFraction = 6- operatingCost = ships * 250- profit = revenue - operatingCost- revenue = totalCatchPerYear * fishPrice- ships <- integ shipBuildingRate 10- let shipBuildingRate = maxDynamics 0 (profit * fractionInvested / shipCost)- shipCost = 300- totalProfit <- integ annualProfit 0- let totalCatchPerYear = maxDynamics 0 (ships * catchPerShip)- -- results --- runDynamicsInStopTime annualProfit--main = runSimulation model specs >>= print
examples/Furnace.hs view
@@ -4,17 +4,15 @@ import Control.Monad import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref-import Simulation.Aivika.Dynamics.UVar-import Simulation.Aivika.Dynamics.Process-import Simulation.Aivika.Dynamics.Random-import Simulation.Aivika.Statistics+import Simulation.Aivika.Event+import Simulation.Aivika.Ref+import Simulation.Aivika.Process+import Simulation.Aivika.Random -import qualified Simulation.Aivika.Queue as Q+import qualified Simulation.Aivika.DoubleLinkedList as DLL -- | The simulation specs. specs = Specs { spcStartTime = 0.0,@@ -38,22 +36,16 @@ -- | Represents the furnace. data Furnace = - Furnace { furnaceQueue :: EventQueue,- -- ^ The event queue.- furnaceNormalGen :: IO Double,+ Furnace { furnaceNormalGen :: IO Double, -- ^ The normal random number generator. furnacePits :: [Pit], -- ^ The pits for ingots.- furnacePitCount :: UVar Int,+ furnacePitCount :: Ref Int, -- ^ The count of active pits with ingots.- furnacePitCountStats :: Ref (SamplingStats Int),- -- ^ The statistics about the active pits.- furnaceAwaitingIngots :: Q.Queue Ingot,+ furnaceAwaitingIngots :: DLL.DoubleLinkedList Ingot, -- ^ The awaiting ingots in the queue.- furnaceQueueCount :: UVar Int,+ furnaceQueueCount :: Ref Int, -- ^ The queue count.- furnaceQueueCountStats :: Ref (SamplingStats Int),- -- ^ The statistics about the queue count. furnaceWaitCount :: Ref Int, -- ^ The count of awaiting ingots. furnaceWaitTime :: Ref Double,@@ -74,9 +66,7 @@ -- | A pit in the furnace to place the ingots. data Pit = - Pit { pitQueue :: EventQueue,- -- ^ The bound dynamics queue.- pitIngot :: Ref (Maybe Ingot),+ Pit { pitIngot :: Ref (Maybe Ingot), -- ^ The ingot in the pit. pitTemp :: Ref Double -- ^ The ingot temperature in the pit.@@ -84,7 +74,7 @@ data Ingot = Ingot { ingotFurnace :: Furnace,- -- ^ Return the furnace.+ -- ^ The furnace. ingotReceiveTime :: Double, -- ^ The time at which the ingot was received. ingotReceiveTemp :: Double,@@ -98,31 +88,26 @@ } -- | Create a furnace.-newFurnace :: EventQueue -> Simulation Furnace-newFurnace queue =- do normalGen <- liftIO normalGen- pits <- sequence [newPit queue | i <- [1..10]]- pitCount <- newUVar queue 0- pitCountStats <- newRef queue emptySamplingStats- awaitingIngots <- liftIO Q.newQueue- queueCount <- newUVar queue 0- queueCountStats <- newRef queue emptySamplingStats- waitCount <- newRef queue 0- waitTime <- newRef queue 0.0- heatingTime <- newRef queue 0.0- h <- newRef queue 1650.0- totalCount <- newRef queue 0- loadCount <- newRef queue 0- unloadCount <- newRef queue 0- unloadTemps <- newRef queue []- return Furnace { furnaceQueue = queue,- furnaceNormalGen = normalGen,+newFurnace :: Simulation Furnace+newFurnace =+ do normalGen <- liftIO newNormalGen+ pits <- sequence [newPit | i <- [1..10]]+ pitCount <- newRef 0+ awaitingIngots <- liftIO DLL.newList+ queueCount <- newRef 0+ waitCount <- newRef 0+ waitTime <- newRef 0.0+ heatingTime <- newRef 0.0+ h <- newRef 1650.0+ totalCount <- newRef 0+ loadCount <- newRef 0+ unloadCount <- newRef 0+ unloadTemps <- newRef []+ return Furnace { furnaceNormalGen = normalGen, furnacePits = pits, furnacePitCount = pitCount,- furnacePitCountStats = pitCountStats, furnaceAwaitingIngots = awaitingIngots, furnaceQueueCount = queueCount,- furnaceQueueCountStats = queueCountStats, furnaceWaitCount = waitCount, furnaceWaitTime = waitTime, furnaceHeatingTime = heatingTime,@@ -133,18 +118,17 @@ furnaceUnloadTemps = unloadTemps } -- | Create a new pit.-newPit :: EventQueue -> Simulation Pit-newPit queue =- do ingot <- newRef queue Nothing- h' <- newRef queue 0.0- return Pit { pitQueue = queue,- pitIngot = ingot,+newPit :: Simulation Pit+newPit =+ do ingot <- newRef Nothing+ h' <- newRef 0.0+ return Pit { pitIngot = ingot, pitTemp = h' } -- | Create a new ingot.-newIngot :: Furnace -> Dynamics Ingot+newIngot :: Furnace -> Event Ingot newIngot furnace =- do t <- time+ do t <- liftDynamics time xi <- liftIO $ furnaceNormalGen furnace h' <- liftIO temprnd let c = 0.1 + (0.05 + xi * 0.01)@@ -156,7 +140,7 @@ ingotCoeff = c } -- | Heat the ingot up in the pit if there is such an ingot.-heatPitUp :: Pit -> Dynamics ()+heatPitUp :: Pit -> Event () heatPitUp pit = do ingot <- readRef (pitIngot pit) case ingot of@@ -166,21 +150,21 @@ -- update the temperature of the ingot. let furnace = ingotFurnace ingot- dt' <- dt+ dt' <- liftDynamics dt h' <- readRef (pitTemp pit) h <- readRef (furnaceTemp furnace) writeRef (pitTemp pit) $ h' + dt' * (h - h') * ingotCoeff ingot -- | Check whether there are ready ingots in the pits.-ingotsReady :: Furnace -> Dynamics Bool+ingotsReady :: Furnace -> Event Bool ingotsReady furnace = fmap (not . null) $ filterM (fmap (>= 2200.0) . readRef . pitTemp) $ furnacePits furnace -- | Try to unload the ready ingot from the specified pit.-tryUnloadPit :: Furnace -> Pit -> Dynamics ()+tryUnloadPit :: Furnace -> Pit -> Event () tryUnloadPit furnace pit = do h' <- readRef (pitTemp pit) when (h' >= 2000.0) $@@ -188,23 +172,20 @@ unloadIngot ingot pit -- | Try to load an awaiting ingot in the specified empty pit.-tryLoadPit :: Furnace -> Pit -> Dynamics () +tryLoadPit :: Furnace -> Pit -> Event () tryLoadPit furnace pit = do let ingots = furnaceAwaitingIngots furnace- flag <- liftIO $ Q.queueNull ingots+ flag <- liftIO $ DLL.listNull ingots unless flag $- do ingot <- liftIO $ Q.queueFront ingots- liftIO $ Q.dequeue ingots- t' <- time- modifyUVar (furnaceQueueCount furnace) (+ (-1))- c <- readUVar (furnaceQueueCount furnace)- modifyRef (furnaceQueueCountStats furnace) $- addSamplingStats c+ do ingot <- liftIO $ DLL.listFirst ingots+ liftIO $ DLL.listRemoveFirst ingots+ t' <- liftDynamics time+ modifyRef (furnaceQueueCount furnace) (+ (-1)) loadIngot (ingot { ingotLoadTime = t', ingotLoadTemp = 400.0 }) pit -- | Unload the ingot from the specified pit. -unloadIngot :: Ingot -> Pit -> Dynamics ()+unloadIngot :: Ingot -> Pit -> Event () unloadIngot ingot pit = do h' <- readRef (pitTemp pit) writeRef (pitIngot pit) Nothing@@ -212,13 +193,11 @@ -- count the active pits let furnace = ingotFurnace ingot- count <- readUVar (furnacePitCount furnace)- writeUVar (furnacePitCount furnace) (count - 1)- modifyRef (furnacePitCountStats furnace) $- addSamplingStats (count - 1)+ count <- readRef (furnacePitCount furnace)+ writeRef (furnacePitCount furnace) (count - 1) -- how long did we heat the ingot up?- t' <- time+ t' <- liftDynamics time modifyRef (furnaceHeatingTime furnace) (+ (t' - ingotLoadTime ingot)) @@ -229,17 +208,15 @@ modifyRef (furnaceUnloadCount furnace) (+ 1) -- | Load the ingot in the specified pit-loadIngot :: Ingot -> Pit -> Dynamics ()+loadIngot :: Ingot -> Pit -> Event () loadIngot ingot pit = do writeRef (pitIngot pit) $ Just ingot writeRef (pitTemp pit) $ ingotLoadTemp ingot -- count the active pits let furnace = ingotFurnace ingot- count <- readUVar (furnacePitCount furnace)- writeUVar (furnacePitCount furnace) (count + 1)- modifyRef (furnacePitCountStats furnace) $- addSamplingStats (count + 1)+ count <- readRef (furnacePitCount furnace)+ writeRef (furnacePitCount furnace) (count + 1) -- decrease the furnace temperature h <- readRef (furnaceTemp furnace)@@ -248,21 +225,19 @@ writeRef (furnaceTemp furnace) $ h + dh -- how long did we keep the ingot in the queue?- t' <- time- when (ingotReceiveTime ingot < t') $- do modifyRef (furnaceWaitCount furnace) (+ 1) - modifyRef (furnaceWaitTime furnace)- (+ (t' - ingotReceiveTime ingot))+ t' <- liftDynamics time+ modifyRef (furnaceWaitCount furnace) (+ 1) + modifyRef (furnaceWaitTime furnace)+ (+ (t' - ingotReceiveTime ingot)) -- count the loaded ingots modifyRef (furnaceLoadCount furnace) (+ 1) -- | Start iterating the furnace processing through the event queue.-startIteratingFurnace :: Furnace -> Dynamics ()+startIteratingFurnace :: Furnace -> Event () startIteratingFurnace furnace = - let queue = furnaceQueue furnace- pits = furnacePits furnace- in enqueueWithIntegTimes queue $+ let pits = furnacePits furnace+ in enqueueEventWithIntegTimes $ do ready <- ingotsReady furnace when ready $ do mapM_ (tryUnloadPit furnace) pits@@ -271,19 +246,19 @@ mapM_ heatPitUp pits -- update the temperature of the furnace- dt' <- dt+ dt' <- liftDynamics dt h <- readRef (furnaceTemp furnace) writeRef (furnaceTemp furnace) $ h + dt' * (2600.0 - h) * 0.2 -- | Return all empty pits.-emptyPits :: Furnace -> Dynamics [Pit]+emptyPits :: Furnace -> Event [Pit] emptyPits furnace = filterM (fmap isNothing . readRef . pitIngot) $ furnacePits furnace -- | Accept a new ingot.-acceptIngot :: Furnace -> Dynamics ()+acceptIngot :: Furnace -> Event () acceptIngot furnace = do ingot <- newIngot furnace @@ -291,14 +266,11 @@ modifyRef (furnaceTotalCount furnace) (+ 1) -- check what to do with the new ingot- count <- readUVar (furnacePitCount furnace)+ count <- readRef (furnacePitCount furnace) if count >= 10 then do let ingots = furnaceAwaitingIngots furnace- liftIO $ Q.enqueue ingots ingot- modifyUVar (furnaceQueueCount furnace) (+ 1)- c <- readUVar (furnaceQueueCount furnace)- modifyRef (furnaceQueueCountStats furnace) $- addSamplingStats c+ liftIO $ DLL.listAddLast ingots ingot+ modifyRef (furnaceQueueCount furnace) (+ 1) else do pit:_ <- emptyPits furnace loadIngot ingot pit @@ -308,12 +280,12 @@ do delay <- liftIO $ exprnd (1.0 / 2.5) holdProcess delay -- we have got a new ingot- liftDynamics $ acceptIngot furnace+ liftEvent $ acceptIngot furnace -- repeat it again processFurnace furnace -- | Initialize the furnace.-initializeFurnace :: Furnace -> Dynamics ()+initializeFurnace :: Furnace -> Event () initializeFurnace furnace = do x1 <- newIngot furnace x2 <- newIngot furnace@@ -345,22 +317,20 @@ -- | The simulation model. model :: Simulation () model =- do queue <- newQueue- furnace <- newFurnace queue- pid <- newProcessID queue+ do furnace <- newFurnace+ pid <- newProcessId -- initialize the furnace and start its iterating in start time- runDynamicsInStartTime $+ runEventInStartTime IncludingCurrentEvents $ do initializeFurnace furnace startIteratingFurnace furnace -- accept input ingots- runDynamicsInStartTime $- do t0 <- starttime- runProcess (processFurnace furnace) pid t0+ runProcessInStartTime IncludingCurrentEvents+ pid (processFurnace furnace) -- run the model in the final time point- runDynamicsInStopTime $+ runEventInStopTime IncludingCurrentEvents $ do -- the ingots c0 <- readRef (furnaceTotalCount furnace) c1 <- readRef (furnaceLoadCount furnace)@@ -386,30 +356,32 @@ putStrLn "" -- the ingots in pits- r2 <- readRef (furnacePitCountStats furnace)+ r2 <- readRef (furnacePitCount furnace) liftIO $ do- putStrLn "The ingots in pits: "- putStrLn $ showSamplingStats r2 2 []+ putStrLn "The ingots in pits (in the final time): "+ putStrLn $ show r2 putStrLn "" -- the queue size- r3 <- readRef (furnaceQueueCountStats furnace)+ r3 <- readRef (furnaceQueueCount furnace) liftIO $ do- putStrLn "The queue size: "- putStrLn $ showSamplingStats r3 2 []+ putStrLn "The queue size (in the final time): "+ putStrLn $ show r3 putStrLn "" -- the mean wait time in the queue- t4 <- readRef (furnaceWaitTime furnace) /- fmap (fromInteger . toInteger)- (readRef (furnaceWaitCount furnace))- + waitTime <- readRef (furnaceWaitTime furnace)+ waitCount <- readRef (furnaceWaitCount furnace)++ let t4 = waitTime / fromIntegral waitCount+ -- the mean heating time- t5 <- readRef (furnaceHeatingTime furnace) /- fmap (fromInteger . toInteger)- (readRef (furnaceUnloadCount furnace))+ heatingTime <- readRef (furnaceHeatingTime furnace)+ unloadCount <- readRef (furnaceUnloadCount furnace)++ let t5 = heatingTime / fromIntegral unloadCount liftIO $ do putStrLn $ "The mean wait time: " ++ show t4
examples/MachRep1.hs view
@@ -18,12 +18,12 @@ import System.Random import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation+import Simulation.Aivika.Event import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref-import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Ref+import Simulation.Aivika.Process upRate = 1.0 / 1.0 -- reciprocal of mean up time repairRate = 1.0 / 0.5 -- reciprocal of mean repair time@@ -40,11 +40,10 @@ model :: Simulation Double model =- do queue <- newQueue- totalUpTime <- newRef queue 0.0+ do totalUpTime <- newRef 0.0 - pid1 <- newProcessID queue- pid2 <- newProcessID queue+ pid1 <- newProcessId+ pid2 <- newProcessId let machine :: Process () machine =@@ -52,21 +51,22 @@ upTime <- liftIO $ exprnd upRate holdProcess upTime finishUpTime <- liftDynamics time- liftDynamics $ + liftEvent $ modifyRef totalUpTime (+ (finishUpTime - startUpTime)) repairTime <- liftIO $ exprnd repairRate holdProcess repairTime machine- - runDynamicsInStartTime $- do t0 <- starttime- runProcess machine pid1 t0- runProcess machine pid2 t0++ runProcessInStartTime IncludingCurrentEvents+ pid1 machine+ + runProcessInStartTime IncludingCurrentEvents+ pid2 machine - runDynamicsInStopTime $+ runEventInStopTime IncludingCurrentEvents $ do x <- readRef totalUpTime- y <- stoptime+ y <- liftDynamics stoptime return $ x / (2 * y) main = runSimulation model specs >>= print
examples/MachRep1EventDriven.hs view
@@ -18,11 +18,11 @@ import System.Random import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref+import Simulation.Aivika.Event+import Simulation.Aivika.Ref upRate = 1.0 / 1.0 -- reciprocal of mean up time repairRate = 1.0 / 0.5 -- reciprocal of mean repair time@@ -39,40 +39,38 @@ model :: Simulation Double model =- do queue <- newQueue- totalUpTime <- newRef queue 0.0+ do totalUpTime <- newRef 0.0 - let machineBroken :: Double -> Dynamics ()+ let machineBroken :: Double -> Event () machineBroken startUpTime = - do finishUpTime <- time+ do finishUpTime <- liftDynamics time modifyRef totalUpTime (+ (finishUpTime - startUpTime)) repairTime <- liftIO $ exprnd repairRate -- enqueue a new event let t = finishUpTime + repairTime- enqueue queue t machineRepaired+ enqueueEvent t machineRepaired - machineRepaired :: Dynamics ()+ machineRepaired :: Event () machineRepaired = - do startUpTime <- time+ do startUpTime <- liftDynamics time upTime <- liftIO $ exprnd upRate -- enqueue a new event let t = startUpTime + upTime- enqueue queue t $ machineBroken startUpTime- - runDynamicsInStartTime $- do t0 <- starttime- -- start the first machine- enqueue queue t0 machineRepaired+ enqueueEvent t $ machineBroken startUpTime++ runEventInStartTime IncludingCurrentEvents $+ do -- start the first machine+ machineRepaired -- start the second machine- enqueue queue t0 machineRepaired- - runDynamicsInStopTime $+ machineRepaired++ runEventInStopTime IncludingCurrentEvents $ do x <- readRef totalUpTime- y <- stoptime+ y <- liftDynamics stoptime return $ x / (2 * y) main = runSimulation model specs >>= print
examples/MachRep1TimeDriven.hs view
@@ -18,11 +18,11 @@ import System.Random import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref+import Simulation.Aivika.Event+import Simulation.Aivika.Ref upRate = 1.0 / 1.0 -- reciprocal of mean up time repairRate = 1.0 / 0.5 -- reciprocal of mean repair time@@ -39,20 +39,19 @@ model :: Simulation Double model =- do queue <- newQueue- totalUpTime <- newRef queue 0.0+ do totalUpTime <- newRef 0.0 - let machine :: Simulation (Dynamics ())+ let machine :: Simulation (Event ()) machine =- do startUpTime <- newRef queue 0.0 + do startUpTime <- newRef 0.0 -- a number of iterations when -- the machine works- upNum <- newRef queue (-1)+ upNum <- newRef (-1) -- a number of iterations when -- the machine is broken- repairNum <- newRef queue (-1)+ repairNum <- newRef (-1) -- create a simulation model return $@@ -69,8 +68,8 @@ do writeRef upNum (-1) -- the machine is broken startUpTime' <- readRef startUpTime- finishUpTime' <- time- dt' <- dt+ finishUpTime' <- liftDynamics time+ dt' <- liftDynamics dt modifyRef totalUpTime $ \a -> a + (finishUpTime' - startUpTime')@@ -82,8 +81,8 @@ repaired = do writeRef repairNum (-1) -- the machine is repaired- t' <- time- dt' <- dt+ t' <- liftDynamics time+ dt' <- liftDynamics dt writeRef startUpTime t' upTime' <- liftIO $ exprnd upRate@@ -97,20 +96,21 @@ | otherwise = repaired result - -- create two machines with type Dynamics ()+ -- create two machines with type Event () m1 <- machine m2 <- machine -- start the time-driven simulation of the machines- -- through the event queue- runDynamicsInStartTime $- do enqueueWithIntegTimes queue m1- enqueueWithIntegTimes queue m2+ runEventInStartTime IncludingCurrentEvents $+ -- in the integration time points+ enqueueEventWithIntegTimes $+ do m1+ m2 -- return the result in the stop time- runDynamicsInStopTime $+ runEventInStopTime IncludingCurrentEvents $ do x <- readRef totalUpTime- y <- stoptime+ y <- liftDynamics stoptime return $ x / (2 * y) main = runSimulation model specs >>= print
examples/MachRep2.hs view
@@ -21,13 +21,14 @@ import Control.Monad import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref-import Simulation.Aivika.Dynamics.Resource-import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Event+import Simulation.Aivika.Ref+import Simulation.Aivika.QueueStrategy+import Simulation.Aivika.Resource+import Simulation.Aivika.Process upRate = 1.0 / 1.0 -- reciprocal of mean up time repairRate = 1.0 / 0.5 -- reciprocal of mean repair time@@ -44,22 +45,20 @@ model :: Simulation (Double, Double) model =- do queue <- newQueue- - -- number of times the machines have broken down- nRep <- newRef queue 0 + do -- number of times the machines have broken down+ nRep <- newRef 0 -- number of breakdowns in which the machine -- started repair service right away- nImmedRep <- newRef queue 0+ nImmedRep <- newRef 0 -- total up time for all machines- totalUpTime <- newRef queue 0.0+ totalUpTime <- newRef 0.0 - repairPerson <- newResource queue 1+ repairPerson <- newResource FCFS 1 - pid1 <- newProcessID queue- pid2 <- newProcessID queue+ pid1 <- newProcessId+ pid2 <- newProcessId let machine :: Process () machine =@@ -67,11 +66,11 @@ upTime <- liftIO $ exprnd upRate holdProcess upTime finishUpTime <- liftDynamics time- liftDynamics $ modifyRef totalUpTime + liftEvent $ modifyRef totalUpTime (+ (finishUpTime - startUpTime)) -- check the resource availability- liftDynamics $+ liftEvent $ do modifyRef nRep (+ 1) n <- resourceCount repairPerson when (n == 1) $@@ -83,15 +82,16 @@ releaseResource repairPerson machine- - runDynamicsInStartTime $- do t0 <- starttime- runProcess machine pid1 t0- runProcess machine pid2 t0++ runProcessInStartTime IncludingCurrentEvents+ pid1 machine++ runProcessInStartTime IncludingCurrentEvents+ pid2 machine - runDynamicsInStopTime $+ runEventInStopTime IncludingCurrentEvents $ do x <- readRef totalUpTime- y <- stoptime+ y <- liftDynamics stoptime n <- readRef nRep nImmed <- readRef nImmedRep return (x / (2 * y),
examples/MachRep3.hs view
@@ -17,13 +17,14 @@ import Control.Monad import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref-import Simulation.Aivika.Dynamics.Resource-import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Event+import Simulation.Aivika.Ref+import Simulation.Aivika.QueueStrategy+import Simulation.Aivika.Resource+import Simulation.Aivika.Process upRate = 1.0 / 1.0 -- reciprocal of mean up time repairRate = 1.0 / 0.5 -- reciprocal of mean repair time@@ -40,33 +41,31 @@ model :: Simulation Double model =- do queue <- newQueue- - -- number of machines currently up- nUp <- newRef queue 2+ do -- number of machines currently up+ nUp <- newRef 2 -- total up time for all machines- totalUpTime <- newRef queue 0.0+ totalUpTime <- newRef 0.0 - repairPerson <- newResource queue 1+ repairPerson <- newResource FCFS 1 - pid1 <- newProcessID queue- pid2 <- newProcessID queue+ pid1 <- newProcessId+ pid2 <- newProcessId - let machine :: ProcessID -> Process ()+ let machine :: ProcessId -> Process () machine pid = do startUpTime <- liftDynamics time upTime <- liftIO $ exprnd upRate holdProcess upTime finishUpTime <- liftDynamics time- liftDynamics $ modifyRef totalUpTime + liftEvent $ modifyRef totalUpTime (+ (finishUpTime - startUpTime)) - liftDynamics $ modifyRef nUp $ \a -> a - 1- nUp' <- liftDynamics $ readRef nUp+ liftEvent $ modifyRef nUp $ \a -> a - 1+ nUp' <- liftEvent $ readRef nUp if nUp' == 1 then passivateProcess- else liftDynamics $+ else liftEvent $ do n <- resourceCount repairPerson when (n == 1) $ reactivateProcess pid@@ -74,19 +73,20 @@ requestResource repairPerson repairTime <- liftIO $ exprnd repairRate holdProcess repairTime- liftDynamics $ modifyRef nUp $ \a -> a + 1+ liftEvent $ modifyRef nUp $ \a -> a + 1 releaseResource repairPerson machine pid - runDynamicsInStartTime $- do t0 <- starttime- runProcess (machine pid2) pid1 t0- runProcess (machine pid1) pid2 t0- - runDynamicsInStopTime $+ runProcessInStartTime IncludingCurrentEvents+ pid1 (machine pid2)++ runProcessInStartTime IncludingCurrentEvents+ pid2 (machine pid1)++ runEventInStopTime IncludingCurrentEvents $ do x <- readRef totalUpTime- y <- stoptime+ y <- liftDynamics stoptime return $ x / (2 * y) main = runSimulation model specs >>= print
examples/TimeOut.hs view
@@ -22,12 +22,12 @@ import Control.Monad import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref-import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Event+import Simulation.Aivika.Ref+import Simulation.Aivika.Process ackRate = 1.0 / 1.0 -- reciprocal of the acknowledge mean time toPeriod = 0.5 -- timeout period@@ -44,58 +44,57 @@ model :: Simulation Double model =- do queue <- newQueue- - -- number of messages sent- nMsgs <- newRef queue 0+ do -- number of messages sent+ nMsgs <- newRef 0 -- number of timeouts which have occured- nTimeOuts <- newRef queue 0+ nTimeOuts <- newRef 0 -- reactivatedCode will 1 if timeout occurred, -- 2 ACK if received- reactivatedCode <- newRef queue 0+ reactivatedCode <- newRef 0 - nodePid <- newProcessID queue+ nodePid <- newProcessId let node :: Process () node =- do liftDynamics $ modifyRef nMsgs $ (+) 1+ do liftEvent $ modifyRef nMsgs $ (+) 1 -- create process IDs- timeoutPid <- liftSimulation $ newProcessID queue- ackPid <- liftSimulation $ newProcessID queue+ timeoutPid <- liftSimulation newProcessId+ ackPid <- liftSimulation newProcessId -- set up the timeout- liftDynamics $ runProcessNow (timeout ackPid) timeoutPid+ liftEvent $ runProcess timeoutPid (timeout ackPid) -- set up the message send/ACK- liftDynamics $ runProcessNow (acknowledge timeoutPid) ackPid+ liftEvent $ runProcess ackPid (acknowledge timeoutPid) passivateProcess- code <- liftDynamics $ readRef reactivatedCode- when (code == 1) $- liftDynamics $ modifyRef nTimeOuts $ (+) 1- liftDynamics $ writeRef reactivatedCode 0+ liftEvent $+ do code <- readRef reactivatedCode+ when (code == 1) $+ modifyRef nTimeOuts $ (+) 1+ writeRef reactivatedCode 0 node - timeout :: ProcessID -> Process ()+ timeout :: ProcessId -> Process () timeout ackPid = do holdProcess toPeriod- liftDynamics $+ liftEvent $ do writeRef reactivatedCode 1 reactivateProcess nodePid cancelProcess ackPid - acknowledge :: ProcessID -> Process ()+ acknowledge :: ProcessId -> Process () acknowledge timeoutPid = do ackTime <- liftIO $ exprnd ackRate holdProcess ackTime- liftDynamics $+ liftEvent $ do writeRef reactivatedCode 2 reactivateProcess nodePid cancelProcess timeoutPid - runDynamicsInStartTime $- runProcessNow node nodePid + runProcessInStartTime IncludingCurrentEvents+ nodePid node - runDynamicsInStopTime $+ runEventInStopTime IncludingCurrentEvents $ do x <- readRef nTimeOuts y <- readRef nMsgs return $ x / y
examples/TimeOutInt.hs view
@@ -20,12 +20,12 @@ import Control.Monad import Control.Monad.Trans +import Simulation.Aivika.Specs+import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics-import Simulation.Aivika.Dynamics.Simulation-import Simulation.Aivika.Dynamics.Base-import Simulation.Aivika.Dynamics.EventQueue-import Simulation.Aivika.Dynamics.Ref-import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Event+import Simulation.Aivika.Ref+import Simulation.Aivika.Process ackRate = 1.0 / 1.0 -- reciprocal of the acknowledge mean time toPeriod = 0.5 -- timeout period@@ -42,41 +42,40 @@ model :: Simulation Double model =- do queue <- newQueue- - -- number of messages sent- nMsgs <- newRef queue 0+ do -- number of messages sent+ nMsgs <- newRef 0 -- number of timeouts which have occured- nTimeOuts <- newRef queue 0+ nTimeOuts <- newRef 0 - nodePid <- newProcessID queue+ nodePid <- newProcessId let node :: Process () node =- do liftDynamics $ modifyRef nMsgs $ (+) 1+ do liftEvent $ modifyRef nMsgs $ (+) 1 -- create the process ID- timeoutPid <- liftSimulation $ newProcessID queue+ timeoutPid <- liftSimulation newProcessId -- set up the timeout- liftDynamics $ runProcessNow timeout timeoutPid+ liftEvent $ runProcess timeoutPid timeout -- wait for ACK, but could be timeout ackTime <- liftIO $ exprnd ackRate holdProcess ackTime- interrupted <- liftDynamics $ processInterrupted nodePid- if interrupted- then liftDynamics $ modifyRef nTimeOuts $ (+) 1- else liftDynamics $ cancelProcess timeoutPid+ liftEvent $+ do interrupted <- processInterrupted nodePid+ if interrupted+ then modifyRef nTimeOuts $ (+) 1+ else cancelProcess timeoutPid node timeout :: Process () timeout = do holdProcess toPeriod- liftDynamics $ interruptProcess nodePid+ liftEvent $ interruptProcess nodePid - runDynamicsInStartTime $- runProcessNow node nodePid + runProcessInStartTime IncludingCurrentEvents+ nodePid node - runDynamicsInStopTime $+ runEventInStopTime IncludingCurrentEvents $ do x <- readRef nTimeOuts y <- readRef nMsgs return $ x / y