packages feed

accelerate-examples-1.2.0.0: examples/smoothlife/SmoothLife.hs

{-# LANGUAGE CPP                 #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators       #-}
--
-- A cellular automata simulation over a smooth domain.
--
--  http://arxiv.org/abs/1111.1567
--  http://sourceforge.net/projects/smoothlife/
--

module SmoothLife
  where

import Config

import qualified Prelude                                as P
import Data.Label
import Data.Array.Accelerate                            as A hiding ( size, clamp )
import Data.Array.Accelerate.Examples.Internal          as A hiding ( nf )
import Data.Array.Accelerate.Math.FFT
import Data.Array.Accelerate.Math.DFT.Centre
import Data.Array.Accelerate.Data.Complex

#ifdef ACCELERATE_CUDA_BACKEND
import Data.Array.Accelerate.CUDA.Foreign
#endif

-- Smooth life
-- ~~~~~~~~~~~

smoothlife
    :: Config
    -> Options
    -> Acc (Matrix R)
    -> Acc (Matrix R)
smoothlife conf _opts aa
  = aa''
  where
    -- A simulation step
    --
    aaf         = fft2D Forward (complex aa)
    nf          = zipWith (*) aaf krf
    mf          = zipWith (*) aaf kdf
    n           = map (\x -> real x / the kflr) (fft2D Inverse nf)
    m           = map (\x -> real x / the kfld) (fft2D Inverse mf)
    aa'         = snm conf sn sm b1 b2 d1 d2 n m
    aa''        = clamp $ zipWith timestepMode aa' aa

    -- simulation parameters
    --
    b           = get1 configRim conf
    (ri,ra)     = get2 configDiscRadius conf
    (b1,b2)     = get2 configBirthInterval conf
    (d1,d2)     = get2 configDeathInterval conf
    (sn,sm)     = get2 configStep conf          -- aka. alpha_n alpha_m
    dt          = get1 configTimestep conf

    timestepMode f g = timestepModes f g P.!! get configTimestepMode conf

    size        = get configWindowSize conf
    sh          = Z:.size:.size

    -- initial state
    --
    kflr        = sum (flatten kr)
    kfld        = sum (flatten kd)
    krf         = fft2D Forward (shift2D (complex kr))
    kdf         = fft2D Forward (shift2D (complex kd))

    kd          = generate (constant sh) (\ix -> 1 - linear (radius ix) ri b)
    kr          = generate (constant sh) (\ix -> let r = radius ix
                                                 in  linear r ri b * (1 - linear r ra b))

    -- kflr''      = constant (kflr' `A.indexArray` Z)
    -- kfld''      = constant (kfld' `A.indexArray` Z)
    -- (kflr', kfld', krf', kdf')
    --             = run (get optBackend opts) $ lift (kflr, kfld, krf, kdf)

    -- Auxiliary
    --
    get1 f c    = constant  $ get f c
    get2 f c    = let (x,y) = get f c in (constant x, constant y)

    complex     = map (\x -> lift (x :+ constant 0))

    radius ix   =
      let Z:.y':.x'   = unlift ix     :: Z :. Exp Int :. Exp Int
          x           = fromIntegral $ x' - constant (size `div` 2)
          y           = fromIntegral $ y' - constant (size `div` 2)
      in
      sqrt (x*x + y*y)

    linear x l u
      = x < l-u/2 ? ( 0.0
      , x > l+u/2 ? ( 1.0
      , (x - l + u / 2) / u ))

    clamp = map
          (\x -> min (max x 0.0) 1.0)

    timestepModes f g
      = [ f
        , g + dt*(2.0*f-1.0)
        , g + dt*(f-g)
        ]


-- Equation 6: s(n,m)
-- ~~~~~~~~~~~~~~~~~~

-- Also a few additional modes as discovered from the source code
--
snm :: Config -> Exp R -> Exp R -> Exp R -> Exp R -> Exp R -> Exp R -> Acc (Matrix R) -> Acc (Matrix R) -> Acc (Matrix R)
snm conf sn sm b1 b2 d1 d2
  = zipWith sigmode
  where
    sigtype     = getSigmoidFunction (get configSigtype conf)
    mixtype     = getSigmoidFunction (get configMixtype conf)
    sigmode n m = sigmodes n m P.!! (get configSigmode conf - 1)

    sigmoid_ab :: Exp R -> Exp R -> Exp R -> Exp R
    sigmoid_ab x a b
      = sigtype x a sn * (1.0 - sigtype x b sn)

    sigmoid_mix :: Exp R -> Exp R -> Exp R -> Exp R
    sigmoid_mix x y m
      = x * (1 - mixtype m 0.5 sm) + y * mixtype m 0.5 sm

    mix :: Exp R -> Exp R -> Exp R -> Exp R
    mix x y a = x * (1 - a) + y*a

    -- available sigmodes
    sigmodes n m
      = [ mix         (sigmoid_ab n b1 b2)
                      (sigmoid_ab n d1 d2) m
        , sigmoid_mix (sigmoid_ab n b1 b2)
                      (sigmoid_ab n d1 d2) m
        , sigmoid_ab n (mix b1 d1 m)
                       (mix b2 d2 m)
        , sigmoid_ab n (sigmoid_mix b1 d1 m)
                       (sigmoid_mix b2 d2 m)]


getSigmoidFunction :: SigmoidFunction -> Exp R -> Exp R -> Exp R -> Exp R
getSigmoidFunction f x a ea
  = let
      -- __expf is CUDA's faster but less precise version of exp.
      cexp =
#ifdef ACCELERATE_CUDA_BACKEND
        foreignExp (CUDAForeignExp [] "__expf") exp
#else
        exp
#endif
    in
    case f of
      Hard      -> x >= a ? (1, 0)
      Smooth    -> 1.0/(1.0+cexp(-(x-a)*4.0/ea))
      Atan      -> atan ((x-a) * pi/ea) / pi + 0.5
      Atancos   -> 0.5 * (0.5 * atan ((x-a) / ea) / pi * cos ((x-a) * 1.4) * 1.1 + 1.0)
      Overshoot -> 0.5 + (1.0 / (1.0 + exp (-(x-a)*4.0/ea)) - 0.5) * (1.0 + exp(-(x-a)*(x-a)/ea/ea))
      Linear    -> bounded (\x' a' ea' -> (x'-a')/ea' + 0.5) x a ea
      Hermite   -> bounded (\x' a' ea' -> let v = (x' - (a'-ea'/2.0))/ea' in v * v * (3.0-2.0*v)) x a ea
      Sin       -> bounded (\x' a' ea' -> sin (pi * (x'-a')/ea') * 0.5 + 0.5) x a ea

  where
    bounded :: (Exp R -> Exp R -> Exp R -> Exp R) -> Exp R -> Exp R -> Exp R -> Exp R
    bounded f' x' a' ea'
      = x' < a'-ea'/2.0 ? ( 0.0
      , x' > a'+ea'/2.0 ? ( 1.0
      , f' x' a' ea' ))