packages feed

dde-0.2.0: bench/R1.hs

{-# LANGUAGE BangPatterns #-}
module R1 where

import qualified Data.Vector.Storable as V
import qualified Data.Vector.Storable.Mutable as VM
import           System.IO.Unsafe ( unsafePerformIO )


model :: Double -> Int -> V.Vector R -> V.Vector R
model hStep delaySamples ttrace0 = ttrace1
  where
    sample0 = Sample 0.0 0.0
    hist0 = V.replicate delaySamples 0.0

    iterator = heun2 (hStep / 2.0) (bandpassRhs (tau, delta) $ (\x -> if x < 0 then -x else 0))

    totalIters = V.length ttrace0

    -- Integrator consumes one additional input
    ttrace = ttrace0 V.++ V.singleton (V.last ttrace0)

    (_, ttrace1) = integrator iterator
                                  delaySamples
                                  totalIters
                                  (sample0, hist0, ttrace)

    (tau, delta) = (2.15625, -3.0)

type R = Double

-- Sample is a vector of any length (x, y, z, ...)
data Sample = Sample {-# UNPACK #-} !R {-# UNPACK #-} !R

getX :: Sample -> R
getX (Sample x _) = x
{-# INLINE getX #-}

-- Pair is used to define exactly a pair of values
data Pair = Pair {-# UNPACK #-} !R {-# UNPACK #-} !R

infixl 6 ..+..
(..+..) :: Sample -> Sample -> Sample
(..+..) (Sample x1 y1) (Sample x2 y2) = Sample (x1 + x2) (y1 + y2)
{-# INLINE (..+..) #-}

infixl 7 .*..
(.*..) :: R -> Sample -> Sample
(.*..) c (Sample x2 y2) = Sample (c * x2) (c * y2)
{-# INLINE (.*..) #-}

heun2
  :: R -> ((Sample, R) -> Sample) -> Sample -> Pair -> Sample
heun2 hOver2 f !x !(Pair x_h x_h2) = x_1
  where
    ! f1 = f (x, x_h)
    ! x_1' = x ..+.. 2 * hOver2 .*.. f1
    ! f2 = f (x_1', x_h2)
    ! x_1 = x ..+.. hOver2 .*.. (f1 ..+.. f2)

bandpassRhs
  :: (R, R) -> (R -> R) -> ((Sample, R) -> Sample)
bandpassRhs !(!ε, !δ) f (!(Sample x y), !x_h) = Sample x' y'
  where
    ! x' = (recip ε) * (-x - δ * y + (f x_h))
    ! y' = x

integrator
  :: (Sample -> Pair -> Sample)
    -> Int
    -> Int
    -> (Sample, V.Vector R, V.Vector R)
    -- ^ Dynamical state, delay history, external forcing
    ->  (Sample, V.Vector R)
integrator iterate1 len total (!xy0, !history0, !input) = unsafePerformIO $ do
    ! v <- VM.new total
    xy <- go v 0 xy0
    history <- V.unsafeFreeze v
    return (xy, history)
  where
    addInput !val !i = val + inputVal
      where ! inputVal = input V.! i
    {-# INLINE addInput #-}
    h i = addInput (history0 `V.unsafeIndex` i) i
    {-# INLINE h #-}
    go !v !i !xy
      | i < len - 1 = do
        let !r = iterate1 xy (Pair (h i) (h $ i + 1))
        VM.unsafeWrite v i (getX r)
        go v (i + 1) r
      | i == total = do
        return xy
      -- Iterations after the initial history has been exhausted
      | otherwise = do
        ! newX0 <- if i == len - 1
                      then return (getX xy0)
                      else VM.unsafeRead v (i - len - 1)
        ! newX <- VM.unsafeRead v (i - len)
        let !r = iterate1 xy (Pair (addInput newX0 i) (addInput newX (i + 1)))
        VM.unsafeWrite v i (getX r)
        go v (i + 1) r
{-# INLINE integrator #-}