packages feed

monad-par-0.1: examples/nbody.hs

{-
 - Modified from code released with:
 - Intel Concurrent Collections for Haskell
 - Copyright (c) 2010, Intel Corporation.
 -}
{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE ExistentialQuantification
   , ScopedTypeVariables
   , BangPatterns
   , NamedFieldPuns 
   , RecordWildCards
   , FlexibleInstances
   , DeriveDataTypeable
   , MagicHash 
   , CPP
  #-}
-- This is INCOMPATIBLE with CncPure..

-- Author: Chih-Ping Chen
-- Ported to Monad-par by Ryan Newton.

-- This program uses CnC to calculate the accelerations of the bodies in a 3D system.  
  
import Control.Monad.Par
import Control.Monad
import Data.Int
import qualified Data.List as List
import qualified Data.Array as A
import GHC.Exts
import System.Environment

type Float3D = (Float, Float, Float)
type UFloat3D = (# Float#, Float#, Float# #)


-- This step generates the bodies in the system.
genVector tag = (tag' * 1.0, tag' * 0.2, tag' * 30.0)
   where tag' = fromIntegral tag

-- We are keeping the idiomatic Haskell version around as well for comparison: 
-- #define IDIOMATIC_VER

-- Only doing the O(N^2) part in parallel:
-- This step computes the accelerations of the bodies.       
compute :: A.Array Int Float3D -> A.Array Int (IVar Float3D) -> Int -> Par ()
compute vecList accels tag =
    do 
       let myvector = vecList A.! (tag-1)
       put (accels A.! tag) (accel myvector vecList)
       where 
             g = 9.8

             multTriple :: Float -> Float3D -> Float3D
             multTriple c ( x,y,z ) = ( c*x,c*y,c*z )

	     pairWiseAccel :: Float3D -> Float3D -> Float3D
             pairWiseAccel (x,y,z) (x',y',z') = let dx = x'-x
                                                    dy = y'-y
                                                    dz = z'-z
                                                    eps = 0.005
						    -- Performance degredation here:
						    distanceSq = dx*dx + dy*dy + dz*dz + eps
						    factor = 1/sqrt(distanceSq * distanceSq * distanceSq)

--                                                in multTriple factor (dx,dy,dz)
                                                in multTriple factor (dx,dy,dz)
#ifdef IDIOMATIC_VER
             sumTriples = foldr (\(x,y,z) (x',y',z') -> (x+x',y+y',z+z')) (0,0,0)
	     accel vector vecList = multTriple g $ sumTriples $ List.map (pairWiseAccel vector) vecList
#else
-- Making this much less idiomatic to avoid allocation:
             (strt,end) = A.bounds vecList

             accel :: Float3D -> (A.Array Int Float3D) -> Float3D
	     accel vector vecList = 

             -- Manually inlining to see if the tuples unbox:
	        let (# sx,sy,sz #) = loop strt 0 0 0
		    loop !i !ax !ay !az
                      | i == end = (# ax,ay,az #)
		      | otherwise = 
                       let ( x,y,z )    = vector
			   ( x',y',z' ) = vecList A.! i

                           (# dx,dy,dz #) = (# x'-x, y'-y, z'-z #)
			   eps = 0.005
			   distanceSq = dx*dx + dy*dy + dz*dz + eps
			   factor = 1/sqrt(distanceSq * distanceSq * distanceSq)

			   (# px,py,pz #) = (# factor * dx, factor * dy, factor *dz #)

		       in loop (i+1) (ax+px) (ay+py) (az+pz)
		in ( g*sx, g*sy, g*sz )
#endif


run :: Int -> [Float3D]
run n = runPar $ 
        do 
	   vars <- sequence$ take n $ repeat new
--           accels  <- A.array (0,n-1) [ (i,) | i <- [0..n-1]]
	   -- Is there a better way to make an array of pvars?
           let accels = A.array (1,n) (zip [1..n] vars)

#ifdef IDIOMATIC_VER
           let initVecs = List.map genVector [1..n]
#else
           let initVecs = A.array (0,n-1) [ (i, genVector i) | i <- [0..n-1] ]
#endif
           
	   forM_ [1..n] $ \ t -> fork (compute initVecs accels t)

           sequence (List.map (\i -> get (accels A.! i)) [1..n])

	  
main = 
    do args <- getArgs 
       let accList = case args of 
                      []  -> run (3::Int)
		      [s] -> run (read s)
       putStrLn $ show (foldl (\sum (x,y,z) -> if x>0 then sum+1 else sum) 0 accList)