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 :: !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
elimListTo :: MonadRandom m => Int -> Int -> Double -> [a] -> m [a]
elimListTo n m r xs
| n == m = return xs
| otherwise = elimListTo (n1) 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
elimList :: MonadRandom m => Int -> Double -> [a] -> m [a]
elimList _ _ [] = return []
elimList n r as = n `seq` do
x <- getRandom
let i = min (n1) $ floor $ logBase r (1 (x * (1 r ^ (n + 1))))
let (xs1, _:xs2) = splitAt i as
return (xs1 ++ xs2)
where q = r ^ (n + 1)
shuffle :: MonadRandom m => Int -> [a] -> m [a]
shuffle n xs = n `seq` case xs of
[] -> return []
xs -> do
i <- getRandomR (0, n1)
let (xs1, x:xs2) = splitAt i xs
xs' <- shuffle (n1) (xs1 ++ xs2)
return (x:xs')