packages feed

concurrent-sa (empty) → 1.0.0

raw patch · 5 files changed

+169/−0 lines, 5 filesdep +MonadRandomdep +basesetup-changed

Dependencies added: MonadRandom, base

Files

+ Control/Concurrent/Annealer.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE RecordWildCards #-}++-- | The traditional simulated annealing is to maintain a current state,+-- and repeatedly perturb it, keeping or discarding the perturbed state+-- depending on the difference in an energy function and a "temperature,"+-- which changes as a function of time.  This concurrent+-- SA implementation maintains a population of current states which are+-- perturbed, and lower-ranked states are deleted according to a temperature+-- function.  It is intended as a lightweight approach to parallelizing+-- optimization problems.+module Control.Concurrent.Annealer  (Annealer, initAnnealer, offerState, getBestState, annealForTime) where++import Control.Concurrent (forkIO, killThread, threadDelay)+import Control.Monad (replicateM)++import Control.Concurrent.Annealer.Population hiding (offerState)+import qualified Control.Concurrent.Annealer.Population as Pop++data Annealer s e = PopAnn {+	solPop :: {-# UNPACK #-} !(Population s e),+	perturb :: s -> IO s}++-- | Returns the current best state in the annealer.+getBestState :: Ord e => Annealer s e -> IO s+getBestState = getBest . solPop++-- | Initializes an annealer.+initAnnealer :: Ord e => (s -> e) -- ^ The energy function for a state.+			-> [s]	-- ^ A collection of initial states.+			-> Int	-- ^ The size at which to maintain the population.+			-> (s -> IO s) +				-- ^ The perturbation function.+			-> IO (Annealer s e)+				-- ^ The annealer.+initAnnealer solScore sols popSize perturb = +	do	solPop <- initPop solScore sols popSize+		return PopAnn{..}++-- | A thread in which states in the annealer's current population are perturbed+-- and offered back into the population.+perturber :: Annealer s e -> IO ()+perturber pop@PopAnn{..} = do+	sol' <- perturb =<< pickState solPop+	Pop.offerState sol' solPop+	perturber pop++-- | Offer a state to the annealer.  Depending on the current+-- population, the state may or may not be kept.+offerState :: s -> Annealer s e -> IO ()+offerState s = Pop.offerState s . solPop++-- | Runs several annealing threads for the specified length of time.+annealForTime :: Ord e => +		Int	-- The number of annealing threads to run.+		-> Int	-- The number of milliseconds until this program will try to stop.+		-> Annealer s e	-- The annealer to run.+		-> IO s	-- The best state in the annealer's population at the time of ending.+annealForTime nThreads t pop = do+	threads <- replicateM nThreads (forkIO (perturber pop))+	threadDelay t+	mapM_ killThread threads+	getBestState pop
+ Control/Concurrent/Annealer/Population.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE NamedFieldPuns, RecordWildCards #-}++module Control.Concurrent.Annealer.Population (Population, offerState, initPop, pickState, getBest) where++import Control.Concurrent+import Control.Monad.Random (MonadRandom (..))++import Data.List+import Data.Ord+import Data.Functor++data Population s e = Pop {+	popSize :: {-# UNPACK #-} !Int,+	solScore :: (s -> e),+	solsVar :: MVar [(s, e)],+	solsChan :: Chan s}++offerState :: s -> Population s e -> IO ()+offerState s Pop{solsChan} = writeChan solsChan s++processChan :: Ord e => Int -> Population s e -> IO ()+processChan t pop@Pop{..} = t `seq` do+	sol <- readChan solsChan+	sols <- takeMVar solsVar+	if length sols < popSize then+		do	putMVar solsVar ((sol, solScore sol):sols)+			processChan t pop+		else do	let r = sqrt (log (fromIntegral t) + 1.0) :: Double+			sols' <- sortBy (flip $ comparing snd) <$> shuffle (popSize + 1) ((sol, solScore sol):sols)+			putMVar solsVar =<< elimListTo (length sols') popSize r sols'+			processChan (t+1) pop++-- | Eliminates elements of the list until it reaches a certain size.  +-- The probability that the ith element will be deleted is a geometric +-- function of i.+elimListTo :: MonadRandom m => Int -> Int -> Double -> [a] -> m [a]+elimListTo n m r xs+	| n == m	= return xs+	| otherwise	= elimListTo (n-1) m r =<< elimList n r xs++pickState :: Population s e -> IO s+pickState Pop{..} = do+	sols <- readMVar solsVar+	i <- getRandomR (0, popSize - 1)+	return (fst (sols !! i))++getBest :: Ord e => Population s e -> IO s+getBest Pop{solsVar} = do+	sols <- readMVar solsVar+	return (fst (minimumBy (comparing snd) sols))++initPop :: Ord e => (s -> e) -> [s] -> Int -> IO (Population s e)+initPop solScore sols popSize = do+	solsVar <- newMVar [(sol, solScore sol) | sol <- sols]+	solsChan <- newChan+	let pop = Pop{..}+	forkIO (processChan 0 pop)+	return pop++{-# SPECIALIZE elimList :: Int -> Double -> [a] -> IO [a] #-}+elimList :: MonadRandom m => Int -> Double -> [a] -> m [a]+elimList _ _ [] = return []+elimList n r as = n `seq` do+	x <- getRandom+	let i = min (n-1) $ floor $ logBase r (1 - (x * (1 - r ^ (n + 1))))+	let (xs1, _:xs2) = splitAt i as+	return (xs1 ++ xs2)+	where	q = r ^ (n + 1)++{-# SPECIALIZE shuffle :: Int -> [a] -> IO [a] #-}+shuffle :: MonadRandom m => Int -> [a] -> m [a]+shuffle n xs = n `seq` case xs of+	[] -> return []+	xs -> do+		i <- getRandomR (0, n-1)+		let (xs1, x:xs2) = splitAt i xs+		xs' <- shuffle (n-1) (xs1 ++ xs2)+		return (x:xs')++{-# RULES+	"[] ++" forall xs . [] ++ xs = xs;+	#-}
+ LICENSE view
@@ -0,0 +1,2 @@+Copyright Louis Wasserman 2010+BSD license
+ Setup.lhs view
@@ -0,0 +1,4 @@+#! /usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMain
+ concurrent-sa.cabal view
@@ -0,0 +1,19 @@+Name:		concurrent-sa+Version:	1.0.0+Category:	Algorithms+Author:		Louis Wasserman+License:	BSD3+License-file:	LICENSE+Stability:	experimental+Synopsis:	Concurrent simulated annealing system.+Description:	An extremely lightweight system for concurrent simulated annealing.+Maintainer:	Louis Wasserman <wasserman.louis@gmail.com>+Build-type:	Simple+cabal-version:  >= 1.6+Library{+  build-depends: base >= 4 && < 5, MonadRandom+  exposed-modules:+    Control.Concurrent.Annealer+  other-modules:+    Control.Concurrent.Annealer.Population+}