{-# LANGUAGE RecursiveDo #-} -- | -- Module : Simulation.Aivika.Transform -- Copyright : Copyright (c) 2009-2017, David Sorokin <david.sorokin@gmail.com> -- License : BSD3 -- Maintainer : David Sorokin <david.sorokin@gmail.com> -- Stability : experimental -- Tested with: GHC 8.0.1 -- -- The module defines something which is most close to the notion of -- analogous circuit as an opposite to the digital one. -- module Simulation.Aivika.Transform (-- * The Transform Arrow Transform(..), -- * Delaying the Transform delayTransform, -- * The Time Transform timeTransform, -- * Differential and Difference Equations integTransform, integTransformEither, sumTransform, sumTransformEither) where import qualified Control.Category as C import Control.Arrow import Control.Monad import Simulation.Aivika.Simulation import Simulation.Aivika.Dynamics import Simulation.Aivika.Dynamics.Memo import Simulation.Aivika.Unboxed import Simulation.Aivika.SystemDynamics -- | It allows representing an analogous circuit as an opposite to -- the digital one. -- -- This is a transform of one time varying function to another usually -- specified in the integration time points and then interpolated in -- other time points with help of one of the memoization functions -- like 'memo0Dynamics'. -- newtype Transform a b = Transform { runTransform :: Dynamics a -> Simulation (Dynamics b) -- ^ Run the transform. } instance C.Category Transform where id = Transform return (Transform g) . (Transform f) = Transform $ \a -> f a >>= g instance Arrow Transform where arr f = Transform $ return . fmap f first (Transform f) = Transform $ \bd -> do (b, d) <- unzip0Dynamics bd c <- f b return $ liftM2 (,) c d second (Transform f) = Transform $ \db -> do (d, b) <- unzip0Dynamics db c <- f b return $ liftM2 (,) d c (Transform f) *** (Transform g) = Transform $ \bb' -> do (b, b') <- unzip0Dynamics bb' c <- f b c' <- g b' return $ liftM2 (,) c c' (Transform f) &&& (Transform g) = Transform $ \b -> do c <- f b c' <- g b return $ liftM2 (,) c c' -- instance ArrowLoop Transform where -- -- loop (Transform f) = -- Transform $ \b -> -- mdo let bd = liftM2 (,) b d -- cd <- f bd -- (c, d) <- unzip0Dynamics cd -- return c -- | A transform that returns the current modeling time. timeTransform :: Transform a Double timeTransform = Transform $ const $ return time -- | Return a delayed transform by the specified lag time and initial value. -- -- This is actually the 'delayI' function wrapped in the 'Transform' type. delayTransform :: Dynamics Double -- ^ the lag time -> Dynamics a -- ^ the initial value -> Transform a a -- ^ the delayed transform delayTransform lagTime init = Transform $ \a -> delayI a lagTime init -- | Return a transform that maps the derivative to an integral -- by the specified initial value. -- -- This is actually the 'integ' function wrapped in the 'Transform' type. integTransform :: Dynamics Double -- ^ the initial value -> Transform Double Double -- ^ map the derivative to an integral integTransform init = Transform $ \diff -> integ diff init -- | Like 'integTransform' but allows either setting a new 'Left' value of the integral, -- or updating it by the specified 'Right' derivative. integTransformEither :: Dynamics Double -- ^ the initial value -> Transform (Either Double Double) Double -- ^ map either a new 'Left' value or the 'Right' derivative to an integral integTransformEither init = Transform $ \diff -> integEither diff init -- | Return a transform that maps the difference to a sum -- by the specified initial value. -- -- This is actually the 'diffsum' function wrapped in the 'Transform' type. sumTransform :: (Num a, Unboxed a) => Dynamics a -- ^ the initial value -> Transform a a -- ^ map the difference to a sum sumTransform init = Transform $ \diff -> diffsum diff init -- | Like 'sumTransform' but allows either setting a new 'Left' value of the sum, -- or updating it by the specified 'Right' difference. sumTransformEither :: (Num a, Unboxed a) => Dynamics a -- ^ the initial value -> Transform (Either a a) a -- ^ map either a new 'Left' value or the 'Right' difference to a sum sumTransformEither init = Transform $ \diff -> diffsumEither diff init