module Simulation.Aivika.Dynamics
(
Dynamics,
DynamicsTrans(..),
Specs(..),
Method(..),
runDynamics1,
runDynamics,
runDynamicsIO,
starttime,
stoptime,
dt,
time,
maxD,
minD,
Integ,
newInteg,
integInit,
integValue,
integDiff,
lookupD,
lookupStepwiseD,
initD,
discrete,
interpolate,
Memo,
UMemo,
memo,
umemo,
memo0,
umemo0,
once,
DynamicsQueue,
newQueue,
enqueueDC,
enqueueD,
runQueue,
DynamicsRef,
newRef,
refQueue,
readRef,
writeRef,
writeRef',
modifyRef,
modifyRef',
DynamicsPID,
DynamicsProc,
newPID,
pidQueue,
holdProcD,
holdProc,
passivateProc,
procPassive,
reactivateProc,
procPID,
runProc,
DynamicsResource,
newResource,
resourceQueue,
resourceInitCount,
resourceCount,
requestResource,
releaseResource,
Agent,
AgentState,
newAgent,
newState,
newSubstate,
agentQueue,
agentState,
activateState,
initState,
stateAgent,
stateParent,
addTimeoutD,
addTimeout,
addTimerD,
addTimer,
stateActivation,
stateDeactivation) where
import Data.Array
import Data.Array.IO
import Data.IORef
import Control.Monad
import Control.Monad.Trans
import qualified Simulation.Aivika.Queue as Q
import qualified Simulation.Aivika.PriorityQueue as PQ
newtype Dynamics a = Dynamics (Parameters -> IO a)
data Parameters = Parameters { parSpecs :: Specs,
parTime :: Double,
parIteration :: Int,
parPhase :: Int }
data Specs = Specs { spcStartTime :: Double,
spcStopTime :: Double,
spcDT :: Double,
spcMethod :: Method
} deriving (Eq, Ord, Show)
data Method = Euler
| RungeKutta2
| RungeKutta4
deriving (Eq, Ord, Show)
iterations :: Specs -> [Int]
iterations sc = [i1 .. i2] where
i1 = 0
i2 = round ((spcStopTime sc
spcStartTime sc) / spcDT sc)
iterationBnds :: Specs -> (Int, Int)
iterationBnds sc = (0, round ((spcStopTime sc
spcStartTime sc) / spcDT sc))
iterationLoBnd :: Specs -> Int
iterationLoBnd sc = 0
iterationHiBnd :: Specs -> Int
iterationHiBnd sc = round ((spcStopTime sc
spcStartTime sc) / spcDT sc)
phases :: Specs -> [Int]
phases sc =
case spcMethod sc of
Euler -> [0]
RungeKutta2 -> [0, 1]
RungeKutta4 -> [0, 1, 2, 3]
phaseBnds :: Specs -> (Int, Int)
phaseBnds sc =
case spcMethod sc of
Euler -> (0, 0)
RungeKutta2 -> (0, 1)
RungeKutta4 -> (0, 3)
phaseLoBnd :: Specs -> Int
phaseLoBnd sc = 0
phaseHiBnd :: Specs -> Int
phaseHiBnd sc =
case spcMethod sc of
Euler -> 0
RungeKutta2 -> 1
RungeKutta4 -> 3
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' = fromInteger (toInteger 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
neighborhood :: Specs -> Double -> Double -> Bool
neighborhood sc t t' =
abs (t t') <= spcDT sc / 1.0e6
instance Monad Dynamics where
return = returnD
m >>= k = bindD m k
returnD :: a -> Dynamics a
returnD a = Dynamics (\ps -> return a)
bindD :: Dynamics a -> (a -> Dynamics b) -> Dynamics b
bindD (Dynamics m) k =
Dynamics $ \ps ->
do a <- m ps
let Dynamics m' = k a
m' ps
subrunDynamics1 :: Dynamics a -> Specs -> IO a
subrunDynamics1 (Dynamics m) sc =
do let n = iterationHiBnd sc
t = basicTime sc n 0
m Parameters { parSpecs = sc,
parTime = t,
parIteration = n,
parPhase = 0 }
subrunDynamics :: Dynamics a -> Specs -> [IO a]
subrunDynamics (Dynamics m) sc =
do let (nl, nu) = iterationBnds sc
parameterise n = Parameters { parSpecs = sc,
parTime = basicTime sc n 0,
parIteration = n,
parPhase = 0 }
map (m . parameterise) [nl .. nu]
runDynamics1 :: Dynamics (Dynamics a) -> Specs -> IO a
runDynamics1 (Dynamics m) sc =
do d <- m Parameters { parSpecs = sc,
parTime = spcStartTime sc,
parIteration = 0,
parPhase = 0 }
subrunDynamics1 d sc
runDynamics :: Dynamics (Dynamics a) -> Specs -> IO [a]
runDynamics (Dynamics m) sc =
do d <- m Parameters { parSpecs = sc,
parTime = spcStartTime sc,
parIteration = 0,
parPhase = 0 }
sequence $ subrunDynamics d sc
runDynamicsIO :: Dynamics (Dynamics a) -> Specs -> IO [IO a]
runDynamicsIO (Dynamics m) sc =
do d <- m Parameters { parSpecs = sc,
parTime = spcStartTime sc,
parIteration = 0,
parPhase = 0 }
return $ subrunDynamics d sc
instance Functor Dynamics where
fmap f (Dynamics m) =
Dynamics $ \ps -> do { a <- m ps; return $ f a }
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
liftMD f (Dynamics x) =
Dynamics $ \ps -> do { a <- x ps; return $ f a }
liftM2D :: (a -> b -> c) -> Dynamics a -> Dynamics b -> Dynamics c
liftM2D f (Dynamics x) (Dynamics y) =
Dynamics $ \ps -> do { a <- x ps; b <- y ps; 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
class DynamicsTrans m where
liftD :: Dynamics a -> m a
starttime :: Dynamics Double
starttime = Dynamics $ return . spcStartTime . parSpecs
stoptime :: Dynamics Double
stoptime = Dynamics $ return . spcStopTime . parSpecs
dt :: Dynamics Double
dt = Dynamics $ return . spcDT . parSpecs
time :: Dynamics Double
time = Dynamics $ return . parTime
maxD :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics a
maxD = liftM2D max
minD :: (Ord a) => Dynamics a -> Dynamics a -> Dynamics a
minD = liftM2D min
data Integ = Integ { integInit :: Dynamics Double,
integExternal :: IORef (Dynamics Double),
integInternal :: IORef (Dynamics Double) }
newInteg :: Dynamics Double -> Dynamics Integ
newInteg i =
do r1 <- liftIO $ newIORef $ initD i
r2 <- liftIO $ newIORef $ initD i
let integ = Integ { integInit = i,
integExternal = r1,
integInternal = r2 }
z = Dynamics $ \ps ->
do (Dynamics m) <- readIORef (integInternal integ)
m ps
y <- umemo interpolate z
liftIO $ writeIORef (integExternal integ) y
return integ
integValue :: Integ -> Dynamics Double
integValue integ =
Dynamics $ \ps ->
do (Dynamics m) <- readIORef (integExternal integ)
m ps
integDiff :: Integ -> Dynamics Double -> Dynamics ()
integDiff integ diff =
do let z = Dynamics $ \ps ->
do y <- readIORef (integExternal integ)
let i = integInit integ
case spcMethod (parSpecs ps) of
Euler -> integEuler diff i y ps
RungeKutta2 -> integRK2 diff i y ps
RungeKutta4 -> integRK4 diff i y ps
liftIO $ writeIORef (integInternal integ) z
integEuler :: Dynamics Double
-> Dynamics Double
-> Dynamics Double
-> Parameters -> IO Double
integEuler (Dynamics f) (Dynamics i) (Dynamics y) ps =
case parIteration ps of
0 ->
i ps
n -> do
let sc = parSpecs ps
ty = basicTime sc (n 1) 0
psy = ps { parTime = ty, parIteration = n 1, parPhase = 0 }
a <- y psy
b <- f psy
let !v = a + spcDT (parSpecs ps) * b
return v
integRK2 :: Dynamics Double
-> Dynamics Double
-> Dynamics Double
-> Parameters -> IO Double
integRK2 (Dynamics f) (Dynamics i) (Dynamics y) ps =
case parPhase ps of
0 -> case parIteration ps of
0 ->
i ps
n -> do
let sc = parSpecs ps
ty = basicTime sc (n 1) 0
t1 = ty
t2 = basicTime sc (n 1) 1
psy = ps { parTime = ty, parIteration = n 1, parPhase = 0 }
ps1 = psy
ps2 = ps { parTime = t2, parIteration = n 1, parPhase = 1 }
vy <- y psy
k1 <- f ps1
k2 <- f ps2
let !v = vy + spcDT sc / 2.0 * (k1 + k2)
return v
1 -> do
let sc = parSpecs ps
n = parIteration ps
ty = basicTime sc n 0
t1 = ty
psy = ps { parTime = ty, parIteration = n, parPhase = 0 }
ps1 = psy
vy <- y psy
k1 <- f ps1
let !v = vy + spcDT sc * k1
return v
_ ->
error "Incorrect phase: integ"
integRK4 :: Dynamics Double
-> Dynamics Double
-> Dynamics Double
-> Parameters -> IO Double
integRK4 (Dynamics f) (Dynamics i) (Dynamics y) ps =
case parPhase ps of
0 -> case parIteration ps of
0 ->
i ps
n -> do
let sc = parSpecs ps
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
psy = ps { parTime = ty, parIteration = n 1, parPhase = 0 }
ps1 = psy
ps2 = ps { parTime = t2, parIteration = n 1, parPhase = 1 }
ps3 = ps { parTime = t3, parIteration = n 1, parPhase = 2 }
ps4 = ps { parTime = t4, parIteration = n 1, parPhase = 3 }
vy <- y psy
k1 <- f ps1
k2 <- f ps2
k3 <- f ps3
k4 <- f ps4
let !v = vy + spcDT sc / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
return v
1 -> do
let sc = parSpecs ps
n = parIteration ps
ty = basicTime sc n 0
t1 = ty
psy = ps { parTime = ty, parIteration = n, parPhase = 0 }
ps1 = psy
vy <- y psy
k1 <- f ps1
let !v = vy + spcDT sc / 2.0 * k1
return v
2 -> do
let sc = parSpecs ps
n = parIteration ps
ty = basicTime sc n 0
t2 = basicTime sc n 1
psy = ps { parTime = ty, parIteration = n, parPhase = 0 }
ps2 = ps { parTime = t2, parIteration = n, parPhase = 1 }
vy <- y psy
k2 <- f ps2
let !v = vy + spcDT sc / 2.0 * k2
return v
3 -> do
let sc = parSpecs ps
n = parIteration ps
ty = basicTime sc n 0
t3 = basicTime sc n 2
psy = ps { parTime = ty, parIteration = n, parPhase = 0 }
ps3 = ps { parTime = t3, parIteration = n, parPhase = 2 }
vy <- y psy
k3 <- f ps3
let !v = vy + spcDT sc * k3
return v
_ ->
error "Incorrect phase: integ"
lookupD :: Dynamics Double -> Array Int (Double, Double) -> Dynamics Double
lookupD (Dynamics m) tbl =
Dynamics (\ps -> do a <- m ps; 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
lookupStepwiseD :: Dynamics Double -> Array Int (Double, Double)
-> Dynamics Double
lookupStepwiseD (Dynamics m) tbl =
Dynamics (\ps -> do a <- m ps; 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
initD :: Dynamics a -> Dynamics a
initD (Dynamics m) =
Dynamics $ \ps ->
if parIteration ps == 0 && parPhase ps == 0 then
m ps
else
let sc = parSpecs ps
in m $ ps { parTime = basicTime sc 0 0,
parIteration = 0,
parPhase = 0 }
discrete :: Dynamics a -> Dynamics a
discrete (Dynamics m) =
Dynamics $ \ps ->
let ph = parPhase ps
r | ph == 0 = m ps
| ph > 0 = let sc = parSpecs ps
n = parIteration ps
in m $ ps { parTime = basicTime sc n 0,
parPhase = 0 }
| otherwise = let sc = parSpecs ps
t = parTime ps
n = parIteration ps
t' = spcStartTime sc + fromIntegral (n + 1) * spcDT sc
n' = if neighborhood sc t t' then n + 1 else n
in m $ ps { parTime = basicTime sc n' 0,
parIteration = n',
parPhase = 0 }
in r
interpolate :: Dynamics Double -> Dynamics Double
interpolate (Dynamics m) =
Dynamics $ \ps ->
if parPhase ps >= 0 then
m ps
else
let sc = parSpecs ps
t = parTime ps
x = (t spcStartTime sc) / spcDT sc
n1 = max (floor x) (iterationLoBnd sc)
n2 = min (ceiling x) (iterationHiBnd sc)
t1 = basicTime sc n1 0
t2 = basicTime sc n2 0
z1 = m $ ps { parTime = t1,
parIteration = n1,
parPhase = 0 }
z2 = m $ ps { parTime = t2,
parIteration = n2,
parPhase = 0 }
r | t == t1 = z1
| t == t2 = z2
| otherwise =
do y1 <- z1
y2 <- z2
return $ y1 + (y2 y1) * (t t1) / (t2 t1)
in r
class (MArray IOArray e IO) => Memo e where
newMemoArray_ :: Ix i => (i, i) -> IO (IOArray i e)
class (MArray IOUArray e IO) => UMemo e where
newMemoUArray_ :: Ix i => (i, i) -> IO (IOUArray i e)
instance Memo e where
newMemoArray_ = newArray_
instance (MArray IOUArray e IO) => UMemo e where
newMemoUArray_ = newArray_
memo :: Memo e => (Dynamics e -> Dynamics e) -> Dynamics e
-> Dynamics (Dynamics e)
memo tr (Dynamics m) =
Dynamics $ \ps ->
do let sc = parSpecs ps
(phl, phu) = phaseBnds sc
(nl, nu) = iterationBnds sc
arr <- newMemoArray_ ((phl, nl), (phu, nu))
nref <- newIORef 0
phref <- newIORef 0
let r ps =
do let sc = parSpecs ps
n = parIteration ps
ph = parPhase ps
phu = phaseHiBnd sc
loop n' ph' =
if (n' > n) || ((n' == n) && (ph' > ph))
then
readArray arr (ph, n)
else
let ps' = ps { parIteration = n', parPhase = ph',
parTime = basicTime sc n' ph' }
in do a <- m ps'
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 $ tr $ Dynamics r
umemo :: UMemo e => (Dynamics e -> Dynamics e) -> Dynamics e
-> Dynamics (Dynamics e)
umemo tr (Dynamics m) =
Dynamics $ \ps ->
do let sc = parSpecs ps
(phl, phu) = phaseBnds sc
(nl, nu) = iterationBnds sc
arr <- newMemoUArray_ ((phl, nl), (phu, nu))
nref <- newIORef 0
phref <- newIORef 0
let r ps =
do let sc = parSpecs ps
n = parIteration ps
ph = parPhase ps
phu = phaseHiBnd sc
loop n' ph' =
if (n' > n) || ((n' == n) && (ph' > ph))
then
readArray arr (ph, n)
else
let ps' = ps { parIteration = n',
parPhase = ph',
parTime = basicTime sc n' ph' }
in do a <- m ps'
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 $ tr $ Dynamics r
memo0 :: Memo e => (Dynamics e -> Dynamics e) -> Dynamics e
-> Dynamics (Dynamics e)
memo0 tr (Dynamics m) =
Dynamics $ \ps ->
do let sc = parSpecs ps
bnds = iterationBnds sc
arr <- newMemoArray_ bnds
nref <- newIORef 0
let r ps =
do let sc = parSpecs ps
n = parIteration ps
loop n' =
if n' > n
then
readArray arr n
else
let ps' = ps { parIteration = n', parPhase = 0,
parTime = basicTime sc n' 0 }
in do a <- m ps'
a `seq` writeArray arr n' a
writeIORef nref (n' + 1)
loop (n' + 1)
n' <- readIORef nref
loop n'
return $ tr $ Dynamics r
umemo0 :: UMemo e => (Dynamics e -> Dynamics e) -> Dynamics e
-> Dynamics (Dynamics e)
umemo0 tr (Dynamics m) =
Dynamics $ \ps ->
do let sc = parSpecs ps
bnds = iterationBnds sc
arr <- newMemoUArray_ bnds
nref <- newIORef 0
let r ps =
do let sc = parSpecs ps
n = parIteration ps
loop n' =
if n' > n
then
readArray arr n
else
let ps' = ps { parIteration = n', parPhase = 0,
parTime = basicTime sc n' 0 }
in do a <- m ps'
a `seq` writeArray arr n' a
writeIORef nref (n' + 1)
loop (n' + 1)
n' <- readIORef nref
loop n'
return $ tr $ Dynamics r
once :: Dynamics a -> Dynamics (Dynamics a)
once (Dynamics m) =
Dynamics $ \ps ->
do x <- newIORef Nothing
let r ps =
do a <- readIORef x
case a of
Just b ->
return b
Nothing ->
do b <- m ps
writeIORef x $ Just b
return $! b
return $ Dynamics r
newtype DynamicsCont a = DynamicsCont (Dynamics (a -> IO ()) -> Dynamics ())
instance Monad DynamicsCont where
return = returnDC
m >>= k = bindDC m k
returnDC :: a -> DynamicsCont a
returnDC a =
DynamicsCont $ \(Dynamics c) ->
Dynamics $ \ps ->
do cont' <- c ps
cont' a
bindDC :: DynamicsCont a -> (a -> DynamicsCont b) -> DynamicsCont b
bindDC (DynamicsCont m) k =
DynamicsCont $ \c ->
m $ Dynamics $ \ps ->
let cont' a = let (DynamicsCont m') = k a
(Dynamics u) = m' c
in u ps
in return cont'
runCont :: DynamicsCont a -> IO (a -> IO ()) -> Dynamics ()
runCont (DynamicsCont m) f = m $ Dynamics $ const f
instance DynamicsTrans DynamicsCont where
liftD (Dynamics m) =
DynamicsCont $ \(Dynamics c) ->
Dynamics $ \ps ->
do cont' <- c ps
a <- m ps
cont' a
instance Functor DynamicsCont where
fmap = liftM
instance MonadIO DynamicsCont where
liftIO m =
DynamicsCont $ \(Dynamics c) ->
Dynamics $ \ps ->
do cont' <- c ps
a <- m
cont' a
data DynamicsQueue = DynamicsQueue {
queuePQ :: PQ.PriorityQueue (Dynamics (() -> IO ())),
queueBusy :: IORef Bool,
queueTime :: IORef Double,
queueRun :: Dynamics () }
newQueue :: Dynamics DynamicsQueue
newQueue =
Dynamics $ \ps ->
do let sc = parSpecs ps
f <- newIORef False
t <- newIORef $ spcStartTime sc
let cont () = return ()
pq <- PQ.newQueue $ return cont
let q = DynamicsQueue { queuePQ = pq,
queueBusy = f,
queueTime = t,
queueRun = subrunQueue q }
return q
enqueueDC :: DynamicsQueue -> Dynamics Double -> Dynamics (() -> IO ())
-> Dynamics ()
enqueueDC q (Dynamics t) c = Dynamics r where
r ps =
do t' <- t ps
let pq = queuePQ q
PQ.enqueue pq t' c
enqueueD :: DynamicsQueue -> Dynamics Double -> Dynamics () -> Dynamics ()
enqueueD q t (Dynamics m) = enqueueDC q t (Dynamics c) where
c ps = let f () = m ps in return f
subrunQueue :: DynamicsQueue -> Dynamics ()
subrunQueue q = Dynamics r where
r ps =
do let f = queueBusy q
f' <- readIORef f
unless f' $
do writeIORef f True
call q ps
writeIORef f False
call q ps =
do let pq = queuePQ q
f <- PQ.queueNull pq
unless f $
do (t2, Dynamics c2) <- PQ.queueFront pq
let t = queueTime q
t' <- readIORef t
when (t2 < t') $
error "The time value is too small: subrunQueue"
when (t2 <= parTime ps) $
do writeIORef t t2
PQ.dequeue pq
let sc = parSpecs ps
t0 = spcStartTime sc
dt = spcDT sc
n2 = fromInteger $ toInteger $ floor ((t2 t0) / dt)
k <- c2 $ ps { parTime = t2,
parIteration = n2,
parPhase = 1 }
k ()
call q ps
runQueue :: DynamicsQueue -> Dynamics ()
runQueue = queueRun
data DynamicsPID =
DynamicsPID { pidQueue :: DynamicsQueue,
pidStarted :: IORef Bool,
pidCont :: IORef (Maybe (Dynamics (() -> IO ()))) }
newtype DynamicsProc a = DynamicsProc (DynamicsPID -> DynamicsCont a)
holdProcD :: Dynamics Double -> DynamicsProc ()
holdProcD t =
DynamicsProc $ \pid ->
DynamicsCont $ \c ->
enqueueDC (pidQueue pid) (t + time) c
holdProc :: Double -> DynamicsProc ()
holdProc t = holdProcD $ return t
passivateProc :: DynamicsProc ()
passivateProc =
DynamicsProc $ \pid ->
DynamicsCont $ \c ->
Dynamics $ \ps ->
do let x = pidCont pid
a <- readIORef x
case a of
Nothing -> writeIORef x $ Just c
Just _ -> error "Cannot passivate the process twice: passivate"
procPassive :: DynamicsPID -> DynamicsProc Bool
procPassive pid =
DynamicsProc $ \_ ->
DynamicsCont $ \(Dynamics c) ->
Dynamics $ \ps ->
do cont' <- c ps
let x = pidCont pid
a <- readIORef x
case a of
Nothing -> cont' False
Just _ -> cont' True
reactivateProc :: DynamicsPID -> DynamicsProc ()
reactivateProc pid =
DynamicsProc $ \pid' ->
DynamicsCont $ \c@(Dynamics cont) ->
Dynamics $ \ps ->
do let x = pidCont pid
a <- readIORef x
case a of
Nothing ->
do cont' <- cont ps
cont' ()
Just (Dynamics cont2) ->
do writeIORef x Nothing
let Dynamics m = enqueueDC (pidQueue pid') time c
m ps
cont2' <- cont2 ps
cont2' ()
runProc :: DynamicsProc () -> DynamicsPID -> Dynamics Double -> Dynamics ()
runProc (DynamicsProc p) pid t =
runCont m r
where m = do y <- liftIO $ readIORef (pidStarted pid)
if y
then error $
"A process with such PID " ++
"has been started already: runProc"
else liftIO $ writeIORef (pidStarted pid) True
DynamicsCont $ \c -> enqueueDC (pidQueue pid) t c
p pid
r = let f () = return () in return f
procPID :: DynamicsProc DynamicsPID
procPID = DynamicsProc $ \pid -> return pid
newPID :: DynamicsQueue -> Dynamics DynamicsPID
newPID q =
do x <- liftIO $ newIORef Nothing
y <- liftIO $ newIORef False
return DynamicsPID { pidQueue = q,
pidStarted = y,
pidCont = x }
instance Eq DynamicsPID where
x == y = pidCont x == pidCont y
instance Monad DynamicsProc where
return = returnDP
m >>= k = bindDP m k
returnDP :: a -> DynamicsProc a
returnDP a = DynamicsProc (\pid -> return a)
bindDP :: DynamicsProc a -> (a -> DynamicsProc b) -> DynamicsProc b
bindDP (DynamicsProc m) k =
DynamicsProc $ \pid ->
do a <- m pid
let DynamicsProc m' = k a
m' pid
instance Functor DynamicsProc where
fmap = liftM
instance DynamicsTrans DynamicsProc where
liftD m = DynamicsProc $ \pid -> liftD m
instance MonadIO DynamicsProc where
liftIO m = DynamicsProc $ \pid -> liftIO m
data DynamicsResource =
DynamicsResource { resourceQueue :: DynamicsQueue,
resourceInitCount :: Int,
resourceCountRef :: IORef Int,
resourceWaitQueue :: Q.Queue (Dynamics (() -> IO ()))}
instance Eq DynamicsResource where
x == y = resourceCountRef x == resourceCountRef y
newResource :: DynamicsQueue -> Int -> Dynamics DynamicsResource
newResource q initCount =
Dynamics $ \ps ->
do countRef <- newIORef initCount
waitQueue <- Q.newQueue
return DynamicsResource { resourceQueue = q,
resourceInitCount = initCount,
resourceCountRef = countRef,
resourceWaitQueue = waitQueue }
resourceCount :: DynamicsResource -> DynamicsProc Int
resourceCount r =
DynamicsProc $ \_ ->
DynamicsCont $ \(Dynamics c) ->
Dynamics $ \ps ->
do cont' <- c ps
a <- readIORef (resourceCountRef r)
cont' a
requestResource :: DynamicsResource -> DynamicsProc ()
requestResource r =
DynamicsProc $ \_ ->
DynamicsCont $ \c@(Dynamics cont) ->
Dynamics $ \ps ->
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'
cont' <- cont ps
cont' ()
releaseResource :: DynamicsResource -> DynamicsProc ()
releaseResource r =
DynamicsProc $ \_ ->
DynamicsCont $ \(Dynamics c) ->
Dynamics $ \ps ->
do a <- readIORef (resourceCountRef r)
let a' = a + 1
when (a' > resourceInitCount r) $
error $
"The resource count cannot be greater than " ++
"its initial value: releaseResource."
f <- Q.queueNull (resourceWaitQueue r)
if f
then a' `seq` writeIORef (resourceCountRef r) a'
else do c2 <- Q.queueFront (resourceWaitQueue r)
Q.dequeue (resourceWaitQueue r)
let Dynamics m = enqueueDC (resourceQueue r) time c2
m ps
cont' <- c ps
cont' ()
data DynamicsRef a =
DynamicsRef { refQueue :: DynamicsQueue,
refRunner :: Dynamics (),
refValue :: IORef a }
newRef :: DynamicsQueue -> a -> Dynamics (DynamicsRef a)
newRef q a =
do x <- liftIO $ newIORef a
return DynamicsRef { refQueue = q,
refRunner = runQueue q,
refValue = x }
readRef :: DynamicsRef a -> Dynamics a
readRef r = Dynamics $ \ps ->
do let Dynamics m = refRunner r
m ps
readIORef (refValue r)
writeRef :: DynamicsRef a -> a -> Dynamics ()
writeRef r a = Dynamics $ \ps ->
do writeIORef (refValue r) a
let Dynamics m = refRunner r
m ps
modifyRef :: DynamicsRef a -> (a -> a) -> Dynamics ()
modifyRef r f = Dynamics $ \ps ->
do let Dynamics m = refRunner r
m ps
modifyIORef (refValue r) f
writeRef' :: DynamicsRef a -> a -> Dynamics ()
writeRef' r a = a `seq` writeRef r a
modifyRef' :: DynamicsRef a -> (a -> a) -> Dynamics ()
modifyRef' r f = Dynamics $ \ps ->
do let Dynamics m = refRunner r
m ps
a <- readIORef (refValue r)
let b = f a
b `seq` writeIORef (refValue r) b
data Agent = Agent { agentQueue :: DynamicsQueue,
agentModeRef :: IORef AgentMode,
agentStateRef :: IORef (Maybe AgentState) }
data AgentState = AgentState { stateAgent :: Agent,
stateParent :: Maybe AgentState,
stateActivateRef :: IORef (Dynamics ()),
stateDeactivateRef :: IORef (Dynamics ()),
stateVersionRef :: IORef Int }
data AgentMode = CreationMode
| InitialMode
| TransientMode
| ProcessingMode
instance Eq Agent where
x == y = agentStateRef x == agentStateRef y
instance Eq AgentState where
x == y = stateVersionRef x == stateVersionRef y
findPath :: AgentState -> AgentState -> ([AgentState], [AgentState])
findPath source target =
if stateAgent source == stateAgent target
then
partitionPath path1 path2
else
error "Different agents: findPath."
where
path1 = fullPath source []
path2 = fullPath target []
fullPath st acc =
case stateParent st of
Nothing -> st : acc
Just st' -> fullPath st' (st : acc)
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)
traversePath :: AgentState -> AgentState -> Dynamics ()
traversePath source target =
let (path1, path2) = findPath source target
agent = stateAgent source
activate st ps =
do Dynamics m <- readIORef (stateActivateRef st)
m ps
deactivate st ps =
do Dynamics m <- readIORef (stateDeactivateRef st)
m ps
in Dynamics $ \ps ->
do writeIORef (agentModeRef agent) TransientMode
forM_ path1 $ \st ->
do writeIORef (agentStateRef agent) (Just st)
deactivate st ps
modifyIORef (stateVersionRef st) (1 +)
forM_ path2 $ \st ->
do when (st == target) $
writeIORef (agentModeRef agent) InitialMode
writeIORef (agentStateRef agent) (Just st)
activate st ps
when (st == target) $
writeIORef (agentModeRef agent) ProcessingMode
addTimeoutD :: AgentState -> Dynamics Double -> Dynamics () -> Dynamics ()
addTimeoutD st t (Dynamics action) =
Dynamics $ \ps ->
do v <- readIORef (stateVersionRef st)
let m1 = Dynamics $ \ps ->
do v' <- readIORef (stateVersionRef st)
when (v == v') $ action ps
q = agentQueue (stateAgent st)
Dynamics m2 = enqueueD q (t + time) m1
m2 ps
addTimerD :: AgentState -> Dynamics Double -> Dynamics () -> Dynamics ()
addTimerD st t (Dynamics action) =
Dynamics $ \ps ->
do v <- readIORef (stateVersionRef st)
let m1 = Dynamics $ \ps ->
do v' <- readIORef (stateVersionRef st)
when (v == v') $ do { m2 ps; action ps }
q = agentQueue (stateAgent st)
Dynamics m2 = enqueueD q (t + time) m1
m2 ps
addTimeout :: AgentState -> Double -> Dynamics () -> Dynamics ()
addTimeout st t = addTimeoutD st (return t)
addTimer :: AgentState -> Double -> Dynamics () -> Dynamics ()
addTimer st t = addTimerD st (return t)
newState :: Agent -> Dynamics AgentState
newState agent =
Dynamics $ \ps ->
do aref <- newIORef $ return ()
dref <- newIORef $ return ()
vref <- newIORef 0
return AgentState { stateAgent = agent,
stateParent = Nothing,
stateActivateRef = aref,
stateDeactivateRef = dref,
stateVersionRef = vref }
newSubstate :: AgentState -> Dynamics AgentState
newSubstate parent =
Dynamics $ \ps ->
do let agent = stateAgent parent
aref <- newIORef $ return ()
dref <- newIORef $ return ()
vref <- newIORef 0
return AgentState { stateAgent = agent,
stateParent = Just parent,
stateActivateRef= aref,
stateDeactivateRef = dref,
stateVersionRef = vref }
newAgent :: DynamicsQueue -> Dynamics Agent
newAgent queue =
Dynamics $ \ps ->
do modeRef <- newIORef CreationMode
stateRef <- newIORef Nothing
return Agent { agentQueue = queue,
agentModeRef = modeRef,
agentStateRef = stateRef }
agentState :: Agent -> Dynamics (Maybe AgentState)
agentState agent =
Dynamics $ \ps ->
do let Dynamics m = queueRun $ agentQueue agent
m ps
readIORef (agentStateRef agent)
activateState :: AgentState -> Dynamics ()
activateState st =
Dynamics $ \ps ->
do let agent = stateAgent st
Dynamics m = queueRun $ agentQueue agent
m ps
mode <- readIORef (agentModeRef agent)
case mode of
CreationMode ->
case stateParent st of
Just _ ->
error $
"To run the agent for the first time, an initial state " ++
"must be top-level: activateState."
Nothing ->
do writeIORef (agentModeRef agent) InitialMode
writeIORef (agentStateRef agent) (Just st)
Dynamics m <- readIORef (stateActivateRef st)
m ps
writeIORef (agentModeRef agent) ProcessingMode
InitialMode ->
error $
"Use the initState function during " ++
"the state activation: activateState."
TransientMode ->
error $
"Use the initState function during " ++
"the state activation: activateState."
ProcessingMode ->
do Just st0 <- readIORef (agentStateRef agent)
let Dynamics m = traversePath st0 st
m ps
initState :: AgentState -> Dynamics ()
initState st =
Dynamics $ \ps ->
do let agent = stateAgent st
Dynamics m = queueRun $ agentQueue agent
m ps
mode <- readIORef (agentModeRef agent)
case mode of
CreationMode ->
error $
"To run the agent for the fist time, use " ++
"the activateState function: initState."
InitialMode ->
do Just st0 <- readIORef (agentStateRef agent)
let Dynamics m = traversePath st0 st
m ps
TransientMode ->
return ()
ProcessingMode ->
error $
"Use the activateState function everywhere outside " ++
"the state activation: initState."
stateActivation :: AgentState -> Dynamics () -> Dynamics ()
stateActivation st action =
Dynamics $ \ps ->
writeIORef (stateActivateRef st) action
stateDeactivation :: AgentState -> Dynamics () -> Dynamics ()
stateDeactivation st action =
Dynamics $ \ps ->
writeIORef (stateDeactivateRef st) action